Markdown
# Clear workspace
rm(list=ls())
# Load libraries
library(skimr)
library(rcompanion)
library(plotly)
library(gridExtra)
library(olsrr)
library(GGally)
library(ggplot2)
library(rcompanion)
# Read in data set
bass <- read.csv(file="bassdata.csv", header=TRUE, sep=",")
names(bass) # `ID` variable was read in with a symbol
[1] "ï..ID" "Lake" "Alkalinity" "pH" "Calcium" "Chlorophyll" "No_samples"
[8] "min" "max" "Mercury" "age_data"
names(bass)[1] <- "ID" # Fix the variable name
names(bass) # Inspect
[1] "ID" "Lake" "Alkalinity" "pH" "Calcium" "Chlorophyll" "No_samples"
[8] "min" "max" "Mercury" "age_data"
Q2 - The Data
This data was collected by a research team led by T.R. Lange in 1993 from 53 lakes in Central Florida to examine some ecological and health concerns resulting from industrial pollution. The unit of observation for this study is a Floridian lake, there are 53 rows and 53 unique entries under the Lake column which are each the name of a unique lake in Florida. The data contained in each row include different measures taken on the lake water and summary statistics about a sample of largemouth bass from that particular lake.
There are 53 rows in this data set, which we will call the bass data set, which means we have data on 53 different Floridian lakes. For each lake, we have information on 11 variables:
ID: A unique number that identifies the body of water. This could be an otherwise meaningless number, but it could also indicate the order in which the observations are collected. It’s exact meaning is unclear, but we will continue as if the former interpretation is true.
Lake: The name of the body of water, some of which appear to refer to bodies other than lakes (like ponds or farms)
Alkalinity: A measure of the alkalinity of the lake water in milligrams per Liter; “the amount of acid needed to bring the sample to a pH of 4.2” (definition from University of Massachusetts Amherst) in mg/L
pH: The pH level of the lake water
Calcium: The calcium concentration of the lake water in milligrams per Liter (mg/L)
Chlorophyll: The chlorophyll concentration of the lake water in micrograms per Liter (written \(\mu\)g/L or mcg/L)
min: The minimum Mercury content found in the sample of largemouth bass from that lake (in micrograms, written \(\mu\)g or mcg)*
max:The maximum Mercury content found in the sample of largemouth bass from that lake (in mcg) *
Mercury: The typical Mercury content found in the sample of largemouth bass from that lake (in mcg) *
No_samples: The sample size of fish (number of largemouth bass) taken from the lake to acquire the mercury statistics *
age_data: context supplies this as the “age” in years, I would assume the typical age in years of bass in the sample from that lake. However, it seems odd that that so many fish would be so young when, apparently, their average lifespan is 16 years. This variable only takes on binary values of 0 and 1, which usually implies a logical indicator. I suspect this variable is a true-false indicator of whether data on the fishes’ age was collected for a given lake. Or, perhaps this 0-1 distinction could be a unitless young-old designation. In any case, the exact meaning and units of this variable are unclear *
Our goal in examining this data is to determine whether the alkalinity levels of a lake might impact Mercury levels in largemouth bass. I am unsure of the conditions under which this data were collected, the lakes in this data may or may not be a representative sample of all lakes in central Florida, and they are most likely not a representative sample of lakes in general. For the sake of this study, we will assume the sample of largemouth bass gathered from each respective lake is a representative sample of all largemouth bass in that lake (although perhaps this is an unreasonable assumption for lakes with the smallest sample sizes). I have no background in chemistry but I imagine the weather on the day of observation could impact all of the numeric variables in the bass data set. Considering how water systems and water cycles work, and the fact that the study was incited by industrial pollution concerns, the location of the lakes themselves could also impact some of the variables in this data set. Both of these are potentially confounding variables. We continue with the analysis with all of this in mind.
Disclaimer
I am inferring the meaning of these starred variables as much as I can based on their names, the focus of the study, and the context of the scenario given. I am only assuming this is their true meaning.
I initially thought ID was meaningless other than serving as an identifier. I assumed that the lakes were arranged in the data set and then an ID was assigned according to the lake’s row number. But eventually I noticed that not every lake’s ID corresponds to their row number, specifically the lakes with IDs between 47 and 51 inclusive. This could just be a scripting mistake, it does seem like lake 51 was just moved up to row 47 and everything was disjointed from there. But there is still the possibility that ID has some meaning.
tail(bass,10)
I was initially unsure of what min and max referred to, but looking at how each lake’s values for these variables are so closely synchronized with the lakes’ values for Mercury led me the the aforementioned interpretation. The plot below displays this. The overall trend is unimportant, but notice that values of Mercury (red line) tend to be associated with similar values of max (blue line) and min (green line). This pattern was not prevalent when replacing Mercury with any other variable in the data. The use of a line plot here isn’t necessarily appropriate since ID is truly categorical, so the corresponding bar chart is also shown, although the pattern isn’t as apparent.
# Line chart
lines <- ggplot(bass) +
geom_line(aes(x=ID,y=min), color="green", lwd=1,alpha=0.6)+
geom_line(aes(x=ID,y=Mercury), color="red", lwd=1,alpha=0.6)+
geom_line(aes(x=ID,y=max), color="blue", lwd=1,alpha=0.6) + ylab("")
# Bar chart
bars <- ggplot(bass) +
geom_col(aes(x=ID,y=max), fill="blue",alpha=0.6)+
geom_col(aes(x=ID,y=Mercury), fill="red",alpha=0.6)+
geom_col(aes(x=ID,y=min), fill="green", alpha=0.6) + ylab("")
grid.arrange(lines,bars,ncol=1)

Examining the Data
Here is a brief look at the data.
head(bass)
Below is a table of summary statistics for the bass data set.
# Display summary statistics of the data
skim(bass)
-- Data Summary ------------------------
Values
Name bass
Number of rows 53
Number of columns 11
_______________________
Column type frequency:
character 1
numeric 10
________________________
Group variables None
-- Variable type: character -------------------------------------------------------------------------
# A tibble: 1 x 8
skim_variable n_missing complete_rate min max empty n_unique whitespace
* <chr> <int> <dbl> <int> <int> <int> <int> <int>
1 Lake 0 1 5 18 0 53 0
-- Variable type: numeric ---------------------------------------------------------------------------
# A tibble: 10 x 11
skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
* <chr> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
1 ID 0 1 27 15.4 1 14 27 40 53 ▇▇▇▇▇
2 Alkalinity 0 1 37.5 38.2 1.2 6.6 19.6 66.5 128 ▇▁▂▁▂
3 pH 0 1 6.59 1.29 3.6 5.8 6.8 7.4 9.1 ▃▅▇▇▃
4 Calcium 0 1 22.2 24.9 1.1 3.3 12.6 35.6 90.7 ▇▂▁▁▁
5 Chlorophyll 0 1 23.1 30.8 0.7 4.6 12.8 24.7 152. ▇▁▁▁▁
6 No_samples 0 1 13.1 8.56 4 10 12 12 44 ▇▁▁▁▁
7 min 0 1 0.280 0.226 0.04 0.09 0.25 0.33 0.92 ▇▆▁▂▁
8 max 0 1 0.875 0.522 0.06 0.48 0.84 1.33 2.04 ▇▇▆▅▂
9 Mercury 0 1 0.513 0.339 0.04 0.25 0.45 0.7 1.53 ▇▇▃▁▁
10 age_data 0 1 0.811 0.395 0 1 1 1 1 ▂▁▁▁▇
Here we create histograms for the meaningful numeric variables (i.e. neither ID nor Lake), which will be shown after the textual description under this code.
# Create histigrams for each meaningful numeric variable
# Histograms will be displayed below summary (the text under this code chunk)
histAlk <- ggplot(bass, aes(x=Alkalinity)) +
geom_histogram(aes(y=..density..), color="grey",fill="white", bins=20)+
geom_density(alpha=0, fill="white",color="red",lwd=1)+
geom_vline(aes(xintercept=mean(Alkalinity)),color="blue", linetype="dashed", size=1)
histpH <- ggplot(bass, aes(x=pH)) +
geom_histogram(aes(y=..density..), color="grey",fill="white", bins=20)+
geom_density(alpha=0, fill="white",color="red",lwd=1) +
geom_vline(aes(xintercept=mean(pH)),color="blue", linetype="dashed", size=1)
histCal <- ggplot(bass, aes(x=Calcium)) +
geom_histogram(aes(y=..density..), color="grey",fill="white", bins=20)+
geom_density(alpha=0, fill="white",color="red",lwd=1)+
geom_vline(aes(xintercept=mean(Calcium)),color="blue", linetype="dashed", size=1)
histChl <- ggplot(bass, aes(x=Chlorophyll)) +
geom_histogram(aes(y=..density..), color="grey",fill="white", bins=20)+
geom_density(alpha=0, fill="white",color="red",lwd=1) +
geom_vline(aes(xintercept=mean(Chlorophyll)),color="blue", linetype="dashed", size=1)
histNS <- ggplot(bass, aes(x=No_samples)) +
geom_histogram(aes(y=..density..), color="grey",fill="white", bins=20)+
geom_density(alpha=0, fill="white",color="red",lwd=1) + xlab("Sample Size") +
geom_vline(aes(xintercept=mean(No_samples)),color="blue", linetype="dashed", size=1)
histAge <- ggplot(bass, aes(x=age_data)) +
geom_histogram(aes(y=..density..), color="grey",fill="white", bins=20)+
geom_density(alpha=0, fill="white",color="red",lwd=1) +
geom_vline(aes(xintercept=mean(age_data)),color="blue", linetype="dashed", size=1)
histMin <- ggplot(bass, aes(x=min)) +
geom_histogram(aes(y=..density..), color="grey",fill="white", bins=20)+
geom_density(alpha=0, fill="white",color="red",lwd=1) + xlab(" Min Mercury") +
geom_vline(aes(xintercept=mean(min)),color="blue", linetype="dashed", size=1)
histMerc <- ggplot(bass, aes(x=Mercury)) +
geom_histogram(aes(y=..density..), color="grey",fill="white", bins=20)+
geom_density(alpha=0, fill="white",color="red",lwd=1) + xlab(" Typical Mercury") +
geom_vline(aes(xintercept=mean(Mercury)),color="blue", linetype="dashed", size=1)
histMax <- ggplot(bass, aes(x=max)) +
geom_histogram(aes(y=..density..), color="grey",fill="white", bins=20)+
geom_density(alpha=0, fill="white",color="red",lwd=1)+ xlab(" Max Mercury") +
geom_vline(aes(xintercept=mean(max)),color="blue", linetype="dashed", size=1)
Alkalinity is heavily positively skewed with a mean of 37.53 mg/L, a median of 19.6 mg/L, and a standard deviation of 38.2 mg/L
pH is roughly symmetric and bell shaped with a slight negative skew. The mean pH level of lakes in this data is 6.59 with a standard deviation of 1.29. The median pH level of the lakes is 6.8.
Calcium has a heavy positive skew with a mean concentration of 22.2 mg/L, a median of 12.6 mg/L, and a standard deviation of 24.93 mg/L
Chlorophyll has a heavy positive skew, a mean of 23.12 mcg/L, a median of 12.8 mcg/L, and a standard deviation of 30.82 mcg/L
- The size of the bass samples for the lakes,
No_sample, is heavily positively skewed with a mean of 13.06 bass, a median of 12 bass, and a standard deviation of 8.56 bass
age_data seems to follow a Bernoulli distribution, all values are either 0 or 1. The mean, median, and standard deviation are 0.81, 1, and 0.39 respectively, although these values aren’t meaningful since we are unsure of the units or context behind this data. ~20% of the lakes have a value of 0 for age_data, ~79% (the rest) of the lakes have a value of 1 (rounding error, there is no missing data).
- The minimum Mercury concentration in bass muscle tissue,
min , is positively skewed with a mean of 0.28 mcg, a median of 0.25 mcg, and a standard deviation of 0.23 mcg
- The typical Mercury concentration in bass muscle tissue,
Mercury , is positively skewed with a mean of 0.51 mcg, a median of 0.45 mcg, and a standard deviation of 0.34 mcg
max: The maximum Mercury concentration in bass muscle tissue, max , is slightly positively skewed with a mean of 0.87 mcg, a median of 0.45 mcg, and a standard deviation of 0.34 mcg
The distributions for these variables and their respective means are shown below.
# Display histograms in a 3 by 3 matrix
grid.arrange(histAlk,histpH,histCal,histChl,histNS,histAge,histMin,histMerc,histMax, ncol=3)

The only categorical variables are ID and Lake, both of which are unique identifying values for the lake that they correspond to. Even though ID is recorded as a number, it is categorical in nature.
There is no missingness in our data.
table(is.na(bass))
FALSE
583
Q3 - Plotting the Data
Do alkalinity levels of a lake impact Mercury levels in largemouth bass? To investigate this research question we examine the alkalinity of lakes in central Florida, Alkalinity, as a predictor of the typical amount of Mercury in a largemouth bass from the lake, Mercury.
# Create scatterplot of Mercury~Alkalinity
scatter<-
ggplot(bass, aes(x=Alkalinity,y=Mercury)) +
geom_point(data=bass, size=3,color="black",fill="blue",shape=21)+
geom_point(data=bass[bass$Lake=="Puzzle ",],size=3,color="black",fill="green",shape=21)+ # Color outlier green
xlab("Lake Alkalinity (mg/L)") +
ylab("Typical Amount of Mercury in Bass (mcg)") +
ggtitle("Mercury Content versus Lake Alkalinity")
# Show interactive plot
ggplotly(scatter)
Above is a plot of lake alkalinity in milligrams per Liter as a predictor of the mercury content in micrograms for largemouth bass from the lake, Mercury~Alkalinity. Each point in the plot above represents one row from the bass data set, data from one lake in central Florida. The green point represents lake 40 or Lake “Puzzle”, a likely outlier lake with bass of unusually high mercury content considering the lake’s alkalinity. There are no other unusual points.
There is a moderately strong, negative association between Mercury and Alkalinity but the relationship between the two is clearly not linear. As is, it would not be appropriate to perform a simple linear regression. In addition to a clear violation of condition that the trend in the data be Linear, the non-linearity is such a dominating trait of the plot that the other conditions cannot be accurately assessed. However, transforming the data may make it appropriate for such a model.
We earlier discussed the potential for confounding variables such as geographical location of the lake and the local weather on the day of observation. We also considered that our data may not be representative of all lakes in Florida depending on how the sample of lakes was chosen, and that our bass sample within one lake may not be representative of all bass in that lake for the especially small bass samples (rows for which No_samples is small). Any trends we do see may not be applicable beyond lakes in central Florida.
Q4 - Initial Model
Here we fit the Simple Linear Regression model for the alkalinity of a lake in central Florida, Alkalinity, as a predictor of the typical amount of Mercury in a largemouth bass from the lake, Mercury. The model summary is printed out below. Beneath the model summary is a scatterplot of Mercury versus Alkalinity including the regression line and the confidence interval around the regression line.
# Fit Mercury~Alkalinity model
bad.model <- ols_regress(Mercury~Alkalinity, data=bass)
#bad.model
scatter.line <-
ggplot(bass, aes(x=Alkalinity,y=Mercury)) +
geom_smooth(data=bass,aes(x=Alkalinity,y=Mercury), method="lm", formula=y~x)+ # plot regression line
geom_point(data=bass, size=3,color="black",fill="blue",shape=21)+
geom_point(data=bass[bass$Lake=="Puzzle ",],size=3,color="black",fill="green",shape=21)+ # Color outlier green
xlab("Lake Alkalinity (mg/L)") +
ylab("Typical Amount of Mercury in Bass (mcg)") +
ggtitle("Mercury Content versus Lake Alkalinity")
ggplotly(scatter.line)
From our SLR model we get the following equation
\[\widehat{Mercury Content}=0.722+(-0.006) \times Alkalinity\]
Where \(MercuryContent\) is the amount of mercury in micrograms in the muscle tissue of a typical fish from a lake and \(Alkalinity\) is the Alkalinity (the amount of acid needed to bring lake water to a pH of 4.2) of a lake in milligrams per Liter. If our SLR assumptions of Linearity, Independence, Normal residuals, and Homoscedasticity were met, then we would interpret the model output as showing a strong, negative linear relationship between the mercury content of a fish and lake alkalinity. Our R-squared value would be telling us that about ~39.4% of the variation in bass mercury content is explained by variation in lake alkalinity. With a p-value of approximately 0 we would have strong evidence to suggest a linear relationship between bass mercury content and lake alkalinity. A lake with alkalinity of 0 mg/L would be expected to have bass with a typical mercury content of 0.722 mcg. And for every additional mg/L of alkalinity in a lake we would expect the typical mercury content of the fish to decrease by about 0.006 mcg. However, we can’t really conclude any of this since our data violates the SLR assumptions.
Q5 - Examining the Model
SLR Assumptions (L.I.N.E)
Here we assess whether our model met the following SLR assumptions:
- Linear data
- Independent errors
- Normally distributed errors
- Homoscedastic errors
To gain inference about the true error terms of our population regression line, we examine the model residuals.
The Residual vs Fit plot clearly shows a curved trend in the residuals. The lack of a random pattern is another indicator that our data was not linear, but we could also see this directly from our scatterplots above. The data is not linear. It also appears that there is more variance in the residuals for lower fitted values than for higher fitted values, our residuals have unequal variance. In other words, our residuals are heteroscedastic.
# Residual vs Fit plot is clearly non-random
ols_plot_resid_fit(bad.model$model)

#grid.arrange(ols_plot_resid_fit(bad.model$model), ols_plot_resid_qq(bad.model$model),ncol=2)
Below we see evidence that our residuals are not normally distributed. The histogram and overlaid density plot show that the residuals are positively skewed. If our residuals were normally distributed then the quantiles of our residuals would closely follow the quantiles of a theoretical normal distribution, a relationship shown by the red line in the Quantile-Quantile (QQ) plot. But our QQ plot is clearly non-linear, showing that our residuals do not follow a normal distribution. This is reaffirmed by the results of hypothesis testing. All of the hypothesis tests below use the following hypotheses:
- \(H_0\): The residuals are normally distributed
- \(H_A\): The residuals are not normally distributed
The majority consensus of several hypothesis tests for normality is to the reject the null hypothesis. With 3 out of 4 of the p-values being approximately 0, we have evidence to suggest that the distribution of our model’s residuals are non-normal. If our error terms were truly normally distributed, it is extremely unlikely that we would get residuals with this distribution.
# Histogram of residuals
ols_plot_resid_hist(bad.model$model)

# Normal probability plot of residuals
ols_plot_resid_qq(bad.model$model)

ols_test_normality(bad.model$model)
-----------------------------------------------
Test Statistic pvalue
-----------------------------------------------
Shapiro-Wilk 0.9077 6e-04
Kolmogorov-Smirnov 0.1151 0.4500
Cramer-von Mises 10.842 0.0000
Anderson-Darling 1.3531 0.0015
-----------------------------------------------
If we had information on the times and locations of these observations then it seems it would be worth investigating serial and spatial correlations just in case the lakes are not independent. In the absence of this information, we can examine the relationships between existing variables for confounding.
Below is a matrix showing the pairwise scatterplot and pairwise correlation between every numeric variable in the data set. Univariate density plots are displayed along the diagonal.
ID seems to have no relationship with any of the other variables, which supports the interpretation that it is a meaningless variable other than as a unique identifier.
As expected, max, min, and Mercury are all highly correlated with each other. They all have significant correlations with each other, signified by the asterisks, and their plots show a clear linear trend. Also note that any other variable with an association with Mercury has the same association with min and max, as seen by the similarity of all of the plots in min , max , and Mercury rows. Again, this was expected. This is evidence that our initial interpretation of the min and max variables was correct.
We also see a trend in the scatterplot of pH versus Alkalinity and a significant correlation between the two, which is also to be expected because pH measures how alkaline or acidic a solution is.
The scatterplot of Mercury versus Calcium and Mercury versus Chlorophyll seem to follow the trend of an exponential decay, a curved, negative trend. However, it also seems that Calcium and Chlorophyll are each respectively interacting with Alkalinity, and they both seem to interact with each another– the corresponding scatterplots all seem to have a positive and roughly linear trend. It is unclear exactly which variable is confounding the other, but there is an interaction occurring between these variables that should be investigated.
# Show correlation matrix for meaningful numeric variables
# Remove 2nd column, Lake name)
ggpairs(bass[,-2], ggplot2::aes(alpha=0.5))

In the plot below we see smaller points tend to be bluer and less alkaline while larger points tend to be more red and more alkaline. Again, each point represents one lake. Chlorophyll and Calcium are mapped to color and size respectively, so the pattern we shows that Chlorophyll, Calcium, and Alkalinity all seem to be following the same negative trend in their interaction with Mercury.
p <- bass %>%
ggplot( aes(x=Alkalinity,y=Mercury, col= Chlorophyll, size=Calcium)) +
geom_point() +
scale_color_gradient(low="blue", high="red")
ggplotly(p)
There is a potential for confounding variables in our data. There is still a possibility of spatial correlation since all of these lakes exist in the vicinity of central Florida. I would not say the independence assumption is upheld.
Unusual Points
Below is a plot of each observational unit’s studentized residual versus its leverage. Recall that a lake is an observational unit, so each point represents the residual and leverage of a particular lake in the data set. Since ID corresponds almost perfectly to the row numbers in the data set (only untrue for rows 47-51) , the number labels shown on the plot identify the lakes by their ID.
ols_plot_resid_lev(bad.model$model)
If we consider a “high leverage” lake to be a lake with a leverage value higher than 3 times the average leverage value, we see that the only high leverage lake is lake 15, a farm lake named “Farm-13”. If we use a more conservative threshold of ~0.075 we see several more lakes with high leverage, listed below. None of these high leverage lakes are outliers, so they are not unduly influencing the model. However, lake 15, the lake named “Farm-13” might be unrepresentative of the population of interest. If we are interested in learning about “wild” lakes we may want to remove lake 15, the “Farm-13” lake from the data. It is not an outlier but it sounds like it is a body of water belonging to a fishing farm and not a naturally occurring lake. It seems inappropriate for “Farm-13” to have so much leverage but not be in our population of interest. We leave it in for now since leverage without influence is largely inconsequential (and I have no evidence other than the name that it is a fishing farm), but the appropriateness of the inclusion of this particular lake should be considered further.
Here are the high leverage points in a table:
# Calculate the cutoff value of leverage
n <- nrow(bass)
k <- length(coefficients(bad.model$model))
print(paste("Conservative Leverage Threshold (Used in plot):", 2* (k/n)))
print(paste("Leverage Threshold:", 3* (k/n)))
bass$bad.model.leverages <- ols_leverage(bad.model$model) # Make a new column in the data for leverage values
bass.lev <- bass[bass$ID %in% c(15,35,3,41,17,37),] # High leverage points only
bass.lev # Show table of high leverage points
If we consider a lake to be an “outlier” if it has a studentized residual greater than 3, only lake 1, lake “Alligator”, is an outlier. If we use a more conservative outlier threshold we can also consider lakes 2, 5, and 40, to be moderate outliers. Lake 40, Lake “Puzzle”, was one we had initially identified as a likely outlier in the scatterplot. But, none of these outliers seem highly influential, as seen by their low leverage values.
Here are the outlier points in a table:
bass$bad.model.residuals <- bad.model$model$residuals # Make a new column in the data for raw residual values
bass.out <- bass[bass$ID %in% c(1,2,5,40),] # Outliers only
bass.out # Show table of outliers
When we directly examine influence with Cook’s Distance we see that lakes 1,2,5, and 40, the same lakes we identified as outliers, have the highest Cook’s Distance. Lake 1 and Lake 40, named Alligator Lake and Puzzle lake respectively, have drastically higher Cook’s distances than most of the other data. These two points have the highest potential to be influential, but neither point has much leverage.
ols_plot_cooksd_bar(bad.model$model)
Here are the outliers (green) and high leverage (red) points on our initial scatterplot (regular points in blue).
color.coded <-
ggplot(bass, aes(x=Alkalinity,y=Mercury)) +
geom_point(data=bass, size=3,color="black",fill="blue",shape=21)+
geom_point(data=bass.out,size=3,color="black",fill="green",shape=21)+ # Outliers
geom_point(data=bass.lev,size=3,color="black",fill="red",shape=21)+ # High leverage points
xlab("Lake Alkalinity (mg/L)") +
ylab("Typical Amount of Mercury in Bass (mcg)") +
ggtitle("Mercury Content versus Lake Alkalinity")
ggplotly(color.coded)
Q6 - Highly Unusual Points
- Alligator Lake has the highest residual mercury value
# Show highest raw residual values in the data
head(arrange(bass, desc(bad.model.residuals)))
- Lake “Farm-13” has the highest leverage
# Show highest leverage values in the data
head(arrange(bass, desc(bad.model.leverages)))
- Puzzle Lake has the highest Cook’s Distance
# Make a new column in the data for cooks distance
bass$bad.model.cooksD <- cooks.distance(bad.model$model)
# Show highest Cook's Distance values in the data
head(arrange(bass, desc(bad.model.cooksD)))
Q7 - Influential Points Discussion
I disagree. While Lake 1 and Lake 40, Alligator Lake and Puzzle Lake respectively, have the largest Cook’s distances values, these same points have small leverage values, which suggests they have little influence on their predicted values. They also have small Cook’s Distances in general , considering the threshold for flagging is usually at around 0.5. These two points have the potential to be influential, but have little leverage. Notice how in the scatterplot below the lakes with high Cooks Distance have low leverage.
# Plot of Cook's Distance versus Leverage
# Notice how points with high Cooks Distance have low leverage
cooks.v.lev <-
ggplot(data=bass, aes(x=bad.model.cooksD,y=bad.model.leverages)) +
geom_point(size=3,color="black",fill="blue",shape=21, alpha=.6)+
xlab("Cooks Distance") +
ylab("Leverage")
# Show interactive plot
ggplotly(cooks.v.lev)
# Leverage and Cook's distance of the outiler points
bass[bass$ID %in% bass.out$ID,][,-2:-12]
If you remove these lakes and refit the model the estimated slope and intercept do not change that much, meaning the removed points don’t have much of an impact on the parameter estimation. These points appear to just be outliers with a high Cook’s Distance.
All of the following models use the formula \(\widehat{Mercury Content}= \text{intercept}+(\text{slope}) \times Alkalinity\)
| bad.model |
None |
None |
0.722166540 |
-0.005567758 |
| linmod.1 |
1 |
Alligator |
0.692194622 |
-0.005205789 |
| linmod.40 |
40 |
Puzzle |
0.726240485 |
-0.006023931 |
| linmod.1.40 |
1 and 40 |
Alligator and Puzzle |
0.69630695 |
-0.00566135 |
# Above table is showing values from these models
# Create 3 new models
lindmod.1 <- lm(Mercury~Alkalinity, bass[-1,]) # Model without lake 1
lindmod.40 <- lm(Mercury~Alkalinity, bass[-40,]) # Model without lake 40
lindmod.1.40 <- lm(Mercury~Alkalinity, bass[-c(1,40),]) # Model without lake 1 or lake 40
Q9 - Transforming the Predictor
Here we create the two models and some plots to asses their fit.
### Creating Models and Plots
## First model: Mercury~ln(Alkalinity)
bass$logAlkalinity <- log(bass$Alkalinity)
lm.logAlk <- ols_regress(Mercury~logAlkalinity,data=bass)
# Add residuals and fits to data
bass$logAlk.resid <- lm.logAlk$model$residuals
bass$logAlk.fits <- lm.logAlk$model$fitted.values
# Model Plot
p.logAlk <-
ggplot(data=bass, aes(x=logAlkalinity, y=Mercury)) +
geom_smooth(method="lm",formula=y~x) +
geom_point(size=3,color="black",fill="blue",shape=21,alpha=0.6) +
ggtitle("Mercury ~ ln(Alkalinity)") +
xlab("ln(Lake Alkalinity) (mg/L)")+
ylab("Typical Amount of Mercury in Bass (mcg)")
# Residual vs Fits Plot
p.logAlk.resid.fits <-
ggplot(data=bass, aes(x=logAlk.fits, y=logAlk.resid)) +
geom_hline(yintercept=0, lty=2, size=1.2) +
geom_point(size=3,color="black",fill="blue",shape=21,alpha=0.6) +
ylab("Residuals") +
xlab("Fits")
# Residual Histogram and Density plot
p.logAlk.hist <-
ggplot(bass, aes(x=logAlk.resid)) +
geom_histogram(aes(y=..density..), color="grey",fill="white", bins=20)+
geom_density(alpha=0.1, fill="blue",color="blue",lwd=1) +
xlab("Residuals")
## Second model: Mercury~ sqrt(Alkalinity)
bass$sqrtAlkalinity <- sqrt(bass$Alkalinity)
lm.sqrtAlk <- ols_regress(Mercury~sqrtAlkalinity,data=bass)
# Add residuals and fits to data
bass$sqrtAlk.resid <- lm.sqrtAlk$model$residuals
bass$sqrtAlk.fits <- lm.sqrtAlk$model$fitted.values
# Model Plot
p.sqrtAlk <-
ggplot(data=bass, aes(x=sqrtAlkalinity, y=Mercury)) +
geom_smooth(method="lm",formula=y~x, color="red") +
geom_point(size=3,color="black",fill="red",shape=21,alpha=0.6) +
ggtitle("Mercury ~ sqrt(Alkalinity)")+
xlab("srqt(Lake Alkalinity) (mg/L)")+
ylab("Typical Amount of Mercury in Bass (mcg)")
# Residuals vs fits
p.sqrtAlk.resid.fits <-
ggplot(data=bass, aes(x=sqrtAlk.fits, y=sqrtAlk.resid)) +
geom_hline(yintercept=0, lty=2, size=1.2) +
geom_point(size=3,color="black",fill="red",shape=21,alpha=0.6) +
ylab("Residuals") +
xlab("Fits")
# Residual Histogram and Density plot
p.sqrtAlk.hist <-
ggplot(bass, aes(x=sqrtAlk.resid)) +
geom_histogram(aes(y=..density..), color="grey",fill="white", bins=20)+
geom_density(alpha=0.1, fill="red",color="red",lwd=1) +
xlab("Residual Distribution")
From The model summaries, the plots, and the comparison Critera such as the AIC score and the Adjusted R-squared values, the model predicting fish mercury content from \(ln(\text{Lake Alkalinity})\) performs better than the model predicting fish mercury content from \(\sqrt{\text{Alkalinity}}\), and both of these models are better than the original model. The models result in the following equations:
\[\widehat{Mercury Content}=1.1173+(-0.2013) \times \ln(Alkalinity)\]
\[\widehat{Mercury Content}=0.9172+(-0.0758) \times \sqrt{Alkalinity}\]
Below are the summaries for each model.
# Display both model summaries
lm.logAlk # Mercury~ln(Alkalinity) model summary
lm.sqrtAlk # Mercury~ sqrt(Alkalinity) model summary
Here we plot the regression line for both models, along with the distribution of their respective residuals. Comparing the scatterplots, the log transformations (blue) seems to have done the best job in making the data linear. The plot which uses the square root transformation (red) still seems to have a curved pattern, the points are mostly above the regression line for highest and lowest values of the predictor. This curved pattern becomes more apparent in the Residuals vs Fits plot for this model (red). The red residuals clearly have a curved, and potentially heteroscedastic, pattern, as well as a clear outlier. The blue residuals also have two potential outliers, but the overall pattern in the plot seems random and homoscedastic.
#Plot results side by side
grid.arrange(p.logAlk, p.sqrtAlk,
p.logAlk.resid.fits, p.sqrtAlk.resid.fits,
p.logAlk.hist, p.sqrtAlk.hist,
ncol=2)
Here we use different information criterion to compare the three models we’ve made so far. We see that the original model predicting bass Mercury content from raw lake Alkalinity, which is named bad.model, performs the worst. bad.model has the highest AIC, AICc, and BIC scores and the lowest R-squared and Adjusted R-squared values, meaning it has the poorest model among the three and it explains the least amount of variation in the data. Looking at the bottom two rows of the comparison output below, we see that the model predicting bass Mercury content from \(\ln( \text{lake Alkalinity})\) (second row) is the best model. It has the lowest AIC, AICc, and BIC scores and explains the most variation in the data (highest R-squared value). The model predicting bass Mercury content from \(\sqrt{\text{lake Alkalinity}}\) (third row) almost explains as much variation in the data as the previous model, but has worse AIC, AICc, and BIC scores.
# Compare both models with the original
compareLM(bad.model$model, lm.logAlk$model, lm.sqrtAlk$model)
Q10 - Examining the Log Transformation
Model Summary
# Display Model Summary
lm.logAlk
p.logAlk
The scatterplot of our transformed data shows a negative linear association between bass mercury content and log lake Alkalinity. By transforming the data, we have eliminated the issue of non-linearity. Now, a simple linear regression is an appropriate way to model out data.
The model predicting bass Mercury content from log lake Alkalinity is:
\[\widehat{Mercury Content}=1.1173+(-0.2013) \times \ln(Alkalinity)\]
Where \(Mercury Content\) is the amount of mercury in mcg found in a typical largemouth bass from a lake and \(\ln(Alkalinity)\) is the natural log of the measure of Alkalinity of a lake in mg/L. With a p-value of almost 0 for the ANOVA F-test for linear association, we have strong evidence to suggest that there is truly a linear relationship between log lake Alkalinity and bass mercury content. For every additional log mg/L Alkalinity of a lake, we expect the mercury content of a typical fish from that lake to decrease by 0.2013 mcg of mercury. This model can account for ~52.6% of the variation in typical bass mercury content with variation in log lake Alkalinity. On average, our model is off by about 0.055 mcg when predicting bass mercury content from log lake alkalinity. Recall that the R-squared and MSE for our initial model with no transformations were 39.49% and 0.71 respectively, a worse performance than this model. Also recall that using this transformation as opposed to the raw data values yields a better model based on the AIC and BIC information criteria as well. This model is a better fit than the original.
SLR Assumptions (L.I.N.E)
p.logAlk.qq <- ols_plot_resid_qq(lm.logAlk$model, print_plot = FALSE)
p.logAlk.resid.fits <- p.logAlk.resid.fits + ggtitle("Residuals vs Fits")
p.logAlk.hist <- p.logAlk.hist + ggtitle("Residual Distribution")
grid.arrange(p.logAlk, p.logAlk.resid.fits,p.logAlk.hist, p.logAlk.qq )
The scatterplot of the transformed data appears to follow a linear trend and the Residuals vs Fits plot appears to have a random pattern, linearity is satisfied with the log transformation.
At first glance there appears to be a slight fanning pattern if you ignore the two possible outliers in the Residual vs. Fits plot, , but an F-test for heteroscedasticity gives us a p-value of ~0.14>0.5=\(\alpha\). We fail to reject the null hypothesis that our data is homoscedastic, there is not strong enough evidence to suggest a deviation from homoscedasticity in our residuals (test output below). We can assume the equal variance condition is satisfied.
# ANOVA F-test for Heteroskedasticity
ols_test_f(lm.logAlk$model)
We can see from the density plot that the residual distribution is positively skewed. The dip below the line in the QQ plot indicates this skew, and the deviation from the line in the upper tails of the distribution indicates that there is more data in the tails of the residual distribution than in a Normal distribution. Statistical tests for normality confirm this. Out of four hypothesis test, each with null hypotheses that our residuals are normally distributed, the majority of the results have a p-value of almost 0 which leads us to reject the null hypothesis (test results below). We have evidence to suggest that our residuals are not normally distributed, which echoes what we saw in the plots. However, the hypothesis test and confidence intervals that gives us our estimates for \(\beta_0\) and \(\beta_1\), the intercept and slope respectively of the regression equation, are robust to non-normal residuals. This deviation from normality may only be of concern if we wish to create prediction intervals, but it is not worth throwing away the model.
ols_test_normality(lm.logAlk$model)
Again, it would be worth investigating serial and spatial correlations but in the absence of this information, we can examine the relationships between existing variables for confounding.
Below is a matrix showing the pairwise scatterplot and pairwise correlation between every meaningful numeric variable in the data set. Univariate density plots are displayed along the diagonal.
As before, max, min, and Mercury are all highly correlated with each other.
Like before with raw Alkalinity, we see a trend in the scatterplot of pH versus logAlkalinity and a significant correlation between the two. This time the relationship is a strong positive linear one as opposed to a curved relationship it has with the raw variable. Again, a relationship between these variables is not surprising considering pH measures acidity and alkalinity.
As before, the scatterplot of Mercury versus Calcium and Mercury versus Chlorophyll seem to follow the trend of an exponential decay, a curved, negative trend. Calcium and Chlorophyll each respectively have a positive association with logAlkalinity and are each interacting with each another. This relationship looked roughly linear with the raw Alkalinity but the relationship with logAlkalinity is curved. It is unclear exactly which variable is confounding the other, but there is an interaction occurring between these variables that should be investigated.
# Show correlation matrix for meaningful numeric variables
# Only use the meaningful numeric variables from the original data set and the transformed ln(Alkalinity) variable
keep <- c("pH","Calcium","Chlorophyll","No_samples","min","max","Mercury","age_data","logAlkalinity","Alkalinity")
ggpairs(bass[,keep], ggplot2::aes(alpha=0.5))
In the plot below each point represents one lake. Lakes with more chlorophyll have more red points and lakes with more calcium are represented by the larger points. We see smaller points tend to be bluer and have lower log alkaline values, while larger points tend to be more red and have higher log alkaline values. So, the pattern we shows that Chlorophyll, Calcium, and logAlkalinity all seem to be following the same negative trend in their interaction with Mercury. We could see this trend when we mapped Chlorophyll and Calcium to color and size on the Mercury~Alkalinity plot from before, but the trend is cleared when take the log of Alkalinity.
p2 <- bass %>%
ggplot( aes(x=logAlkalinity,y=Mercury, col= Chlorophyll, size=Calcium)) +
geom_point() + xlab("ln(Lake Alkalinity) (in mg/L)") +
scale_color_gradient(low="blue", high="red")
ggplotly(p2)
There is still a potential for confounding variables in our data. Also, the possibility of spatial correlation seems very plausible since all of these lakes exist in the vicinity of central Florida. Without any way to assess this, I would not be comfortable in assuming independence is upheld.
Unusual Points
Below is a plot of each observational unit’s studentized residual versus its leverage. Recall that a lake is an observational unit, so each point represents the residual and leverage of a particular lake in the data set. Since ID corresponds almost perfectly to the row numbers in the data set (only untrue for rows 47-51) , the number labels shown on the plot identify the lakes by their ID.
ols_plot_resid_lev(lm.logAlk$model)
We see that the only high leverage lake is in row 50, lake 49 or Tsala Apopka Lake. Lakes 1,2, and 40, Alligator, Annie, and Puzzle Lake respectively, are outliers. We have no highly influential points, but there is one unidentified point that may be classified as such if more conservative thresholds are chosen.
Note that there is one unlabeled blue point on the plot above that is very close to crossing both the leverage and residual threshold, lake 5 or Brick Lake. This is the only point that is close to being an influential point.
Here are the high leverage points in a table, including Brick Lake:
# Calculate the cutoff value of leverage
n <- nrow(bass)
k <- length(coefficients(lm.logAlk$model))
print(paste("Conservative Leverage Threshold (Used in plot):", 2* (k/n)))
print(paste("Leverage Threshold:", 3* (k/n)))
bass$lm.logAlk.leverages <- ols_leverage(lm.logAlk$model) # Make a new column in the data for leverage values
bass.lev <- bass[bass$lm.logAlk.leverages>0.07,] # High leverage points only
bass.lev
Here are the outlier points in a table, including Brick Lake:
bass$lm.logAlk.residuals <- lm.logAlk$model$residuals # Make a new column in the data for raw residual values
bass.out <- bass[bass$ID %in% c(1,40,2,5),] # Outliers only
bass.out
When we directly examine influence with Cook’s Distance we see that lakes 1,2,5, and 40, the same lakes we identified as outliers plus Brick lake, have the highest Cook’s Distance. Lake 1 and Lake 40, named Alligator Lake and Puzzle lake respectively, have drastically higher Cook’s distances than most of the other data. These two points have the highest potential to be influential, but neither point has much leverage. Brick Lake has a moderately high leverage and a moderately high studentized residual, as well as a relatively high Cook’s Distance. Brick lake seems to be a moderatley influential point.
ols_plot_cooksd_bar(lm.logAlk$model)
Here are the outliers (green) and high leverage (red) points on our initial scatterplot (regular points in blue). Brick lake is shown as a yellow diamond.
brick <- bass[5,]
color.coded.2 <-
ggplot(bass[-5,], aes(x=logAlkalinity,y=Mercury)) +
geom_point(data=bass[-5,], size=3,color="black",fill="blue",shape=21)+
geom_point(data=bass.out[-3,],size=3,color="black",fill="green",shape=21)+ # Outliers
geom_point(data=bass.lev[-1,],size=3,color="black",fill="red",shape=21)+ # High leverage points
geom_point(data=brick,size=3,color="black",fill="yellow",shape=23)+
xlab("ln(Lake Alkalinity) ( in mg/L)") +
ylab("Typical Amount of Mercury in Bass (mcg)") +
ggtitle("Mercury Content versus Lake Alkalinity")
ggplotly(color.coded.2)
Q11 - Advice for the Public
To predict the plausible mercury values for largemouth bass from a new lake, we will create a prediction interval with a 99% confidence level. Note that because our residuals were positively skewed, this prediction interval is probably going to capture lower mercury levels that is should not be capturing and it will not capture some higher mercury levels that is should capture. We continue with caution.
new.lake <- data.frame(logAlkalinity=c(log(40)))
pred <- predict(lm.logAlk$model,newdata=new.lake,interval="predict",level=0.99)
pred
If the residuals were normally distributed we would conclude with 99% confidence that the range of plausible values for mercury content in a largemouth bass from this new lake is less than or equal to 1.012 mcg of mercury (we would have the inclusive interval from -0.26 to 1.01, but a negative mercury level is not interpretable). However the true distribution of our residuals seemed positively skewed, not Normal as was assumed in calculating this prediction interval, so this interval isn’t capturing some higher mercury values that it should. In any case, the interval already included 1 mcg mercury, so we would not conclude largemouth bass from this new lake to be safe to eat anyway. This person should not eat largemouth bass from this lake.
We can’t extend this conclusion to other fish species, so unless the fish that this person caught was also a largemouth bass we don’t have the right information to accurately answer their question.
Q12 - Safety Threshold for Alkalinity
We’ll compute the prediction standard error, \(se(prediction)\) , by using the interval above. As a reminder, this is the interval we hve:
pred
So we have this formula:
\[\text{Prediction Interval}= prediction \pm t^*_{\alpha/2,n-2} \times se(prediction)\]
\[\text{PI}_{\text{upper limit}} = prediction + t^*_{\alpha/2,n-2} \times se(prediction)\]
\[1.012571 \mu g = 0.3747508 + t^*_{\alpha/2,n-2} \times se(prediction)\]
We have a significance level of \(\alpha\)=0.05 and \(n-2\)=51 degrees of freedom, so we can find our critical value \(t*\) easily.
alpha <- 0.05
df <- nrow(bass)-2
t.star <- qt(1- (alpha/2), df)
print(paste("t*:", t.star))
\[1.012571 = 0.3747508 + 2.007584 \times se(prediction)\]
\[1.012571 = 0.3747508 + 2.007584 \times se(prediction)\]
\[0.6378202 = 2.007584 \times se(prediction)\]
\[0.3177054 = se(prediction)\]
Now that we have a set critical value and a standard error of prediction, we can find the predicted mercury value that has a prediction interval whose upper limit is 1 mcg of mercury. So we are solving for \(prediction\) in the equation below.
\[\text{PI}_{\text{upper limit}} = prediction + t^*_{\alpha/2,n-2} \times se(prediction)\]
\[1 \mu g = prediction + t^*_{\alpha/2,n-2} \times se(prediction)\]
\[1 \mu g = prediction + 2.007584 \times 0.3177054\]
\[1 \mu g = prediction + 0.6378202\]
\[0.3621798 \mu g = prediction\]
Now we solve for the alkalinity of a lake whose fish have a predicted mercury content of ~0.362 mcg of mercury. To solve for this, we go back to our regression equation.
\[\widehat{Mercury Content}=1.1173+(-0.2013) \times \ln(Alkalinity)\]
\[0.3621798 = 1.1173+(-0.2013) \times \ln(Alkalinity)\]
\[0.3621798 = 1.1173+(-0.2013) \times \ln(Alkalinity)\]
\[-0.7551202 = -0.2013 \times \ln(Alkalinity)\]
\[3.751218 = \ln(Alkalinity)\]
\[e^{3.751218} = e^{\ln(Alkalinity)}\]
\[42.5729 \text{mg/L} = Alkalinity\]
We would set the cutoff of the lake alkalinity to be ~ 42.57 mg/L .
LS0tDQp0aXRsZTogIlNUQVQtNDYyOiBMYWIgNiINCmF1dGhvcjogIkNhbmRhY2UgVG9kZCINCmRhdGU6ICJNYXJjaCAyNCwgMjAyMSINCm91dHB1dDoNCiAgcHJldHR5ZG9jOjpodG1sX3ByZXR0eToNCiAgICB0aGVtZTogbGVvbmlkcw0KICAgIGhpZ2hsaWdodDogZ2l0aHViDQogICAgdG9jOiB0cnVlDQogICAgcHJldHR5ZG9jOjp0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiBsdW1lbiAgICANCi0tLQ0KDQojIE1hcmtkb3duDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KCWZpZy5oZWlnaHQgPSA2LA0KCWZpZy53aWR0aCA9IDEwDQopDQpgYGANCg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KIyBDbGVhciB3b3Jrc3BhY2UNCnJtKGxpc3Q9bHMoKSkNCg0KIyBMb2FkIGxpYnJhcmllcw0KbGlicmFyeShza2ltcikNCmxpYnJhcnkocmNvbXBhbmlvbikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KG9sc3JyKQ0KbGlicmFyeShHR2FsbHkpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHJjb21wYW5pb24pDQpgYGANCg0KYGBge3J9DQojIFJlYWQgaW4gZGF0YSBzZXQNCmJhc3MgPC0gcmVhZC5jc3YoZmlsZT0iYmFzc2RhdGEuY3N2IiwgaGVhZGVyPVRSVUUsIHNlcD0iLCIpDQpuYW1lcyhiYXNzKSAgICAgICAgICAgICMgYElEYCB2YXJpYWJsZSB3YXMgcmVhZCBpbiB3aXRoIGEgc3ltYm9sDQpuYW1lcyhiYXNzKVsxXSA8LSAiSUQiICMgRml4IHRoZSB2YXJpYWJsZSBuYW1lDQpuYW1lcyhiYXNzKSAgICAgICAgICAgICMgSW5zcGVjdA0KYGBgDQojIFEyIC0gVGhlIERhdGENCiAgDQpUaGlzIGRhdGEgd2FzIGNvbGxlY3RlZCBieSBhIHJlc2VhcmNoIHRlYW0gbGVkIGJ5IFQuUi4gTGFuZ2UgaW4gMTk5MyBmcm9tIDUzIGxha2VzIGluIENlbnRyYWwgRmxvcmlkYSB0byBleGFtaW5lIHNvbWUgZWNvbG9naWNhbCBhbmQgaGVhbHRoIGNvbmNlcm5zIHJlc3VsdGluZyBmcm9tIGluZHVzdHJpYWwgcG9sbHV0aW9uLiBUaGUgdW5pdCBvZiBvYnNlcnZhdGlvbiBmb3IgdGhpcyBzdHVkeSBpcyBhIEZsb3JpZGlhbiBsYWtlLCB0aGVyZSBhcmUgNTMgcm93cyBhbmQgNTMgdW5pcXVlIGVudHJpZXMgdW5kZXIgdGhlIGBMYWtlYCBjb2x1bW4gd2hpY2ggYXJlIGVhY2ggdGhlIG5hbWUgb2YgYSB1bmlxdWUgbGFrZSBpbiBGbG9yaWRhLiBUaGUgZGF0YSBjb250YWluZWQgaW4gZWFjaCByb3cgaW5jbHVkZSBkaWZmZXJlbnQgbWVhc3VyZXMgdGFrZW4gb24gdGhlIGxha2Ugd2F0ZXIgYW5kIHN1bW1hcnkgc3RhdGlzdGljcyBhYm91dCBhIHNhbXBsZSBvZiBsYXJnZW1vdXRoIGJhc3MgZnJvbSB0aGF0IHBhcnRpY3VsYXIgbGFrZS4NCg0KVGhlcmUgYXJlIDUzIHJvd3MgaW4gdGhpcyBkYXRhIHNldCwgd2hpY2ggd2Ugd2lsbCBjYWxsIHRoZSBgYmFzc2AgZGF0YSBzZXQsIHdoaWNoIG1lYW5zIHdlIGhhdmUgZGF0YSBvbiA1MyBkaWZmZXJlbnQgRmxvcmlkaWFuIGxha2VzLiBGb3IgZWFjaCBsYWtlLCB3ZSBoYXZlIGluZm9ybWF0aW9uIG9uIDExIHZhcmlhYmxlczoNCiAgDQogICogYElEYDogQSB1bmlxdWUgbnVtYmVyIHRoYXQgaWRlbnRpZmllcyB0aGUgYm9keSBvZiB3YXRlci4gVGhpcyBjb3VsZCBiZSBhbiBvdGhlcndpc2UgbWVhbmluZ2xlc3MgbnVtYmVyLCBidXQgaXQgY291bGQgYWxzbyBpbmRpY2F0ZSB0aGUgb3JkZXIgaW4gd2hpY2ggdGhlIG9ic2VydmF0aW9ucyBhcmUgY29sbGVjdGVkLiBJdCdzIGV4YWN0IG1lYW5pbmcgaXMgdW5jbGVhciwgYnV0IHdlIHdpbGwgY29udGludWUgYXMgaWYgdGhlIGZvcm1lciBpbnRlcnByZXRhdGlvbiBpcyB0cnVlLg0KICAqIGBMYWtlYDogVGhlIG5hbWUgb2YgdGhlIGJvZHkgb2Ygd2F0ZXIsIHNvbWUgb2Ygd2hpY2ggYXBwZWFyIHRvIHJlZmVyIHRvIGJvZGllcyBvdGhlciB0aGFuIGxha2VzIChsaWtlIHBvbmRzIG9yIGZhcm1zKQ0KICAqIGBBbGthbGluaXR5YDogQSBtZWFzdXJlIG9mIHRoZSBhbGthbGluaXR5IG9mIHRoZSBsYWtlIHdhdGVyIGluIG1pbGxpZ3JhbXMgcGVyIExpdGVyOyAidGhlIGFtb3VudCBvZiBhY2lkIG5lZWRlZCB0byBicmluZyB0aGUgc2FtcGxlIHRvIGEgcEggb2YgNC4yIiAoZGVmaW5pdGlvbiBmcm9tIFtVbml2ZXJzaXR5IG9mIE1hc3NhY2h1c2V0dHMgQW1oZXJzdF0oaHR0cHM6Ly93cnJjLnVtYXNzLmVkdS9yZXNlYXJjaC9wcm9qZWN0cy9hY2lkLXJhaW4tbW9uaXRvcmluZy1wcm9qZWN0L2FuYWx5c2lzLW1ldGhvZC1waC1hbmQtYWxrYWxpbml0eSkpIGluIG1nL0wgDQogICogYHBIYDogVGhlIHBIIGxldmVsIG9mIHRoZSBsYWtlIHdhdGVyDQogICogYENhbGNpdW1gOiBUaGUgY2FsY2l1bSBjb25jZW50cmF0aW9uIG9mIHRoZSBsYWtlIHdhdGVyIGluIG1pbGxpZ3JhbXMgcGVyIExpdGVyIChtZy9MKQ0KICAqIGBDaGxvcm9waHlsbGA6IFRoZSBjaGxvcm9waHlsbCBjb25jZW50cmF0aW9uIG9mIHRoZSBsYWtlIHdhdGVyIGluIG1pY3JvZ3JhbXMgcGVyIExpdGVyICh3cml0dGVuICRcbXUkZy9MIG9yIG1jZy9MKQ0KICAqIGBtaW5gOiBUaGUgbWluaW11bSBNZXJjdXJ5IGNvbnRlbnQgZm91bmQgaW4gdGhlIHNhbXBsZSBvZiBsYXJnZW1vdXRoIGJhc3MgZnJvbSB0aGF0IGxha2UgKGluIG1pY3JvZ3JhbXMsIHdyaXR0ZW4gJFxtdSRnIG9yIG1jZylcKg0KICAqIGBtYXhgOlRoZSBtYXhpbXVtIE1lcmN1cnkgY29udGVudCBmb3VuZCBpbiB0aGUgc2FtcGxlIG9mIGxhcmdlbW91dGggYmFzcyBmcm9tIHRoYXQgbGFrZSAoaW4gbWNnKSBcKg0KICAqIGBNZXJjdXJ5YDogVGhlIHR5cGljYWwgTWVyY3VyeSBjb250ZW50IGZvdW5kIGluIHRoZSBzYW1wbGUgb2YgbGFyZ2Vtb3V0aCBiYXNzIGZyb20gdGhhdCBsYWtlIChpbiBtY2cpIFwqDQogICogYE5vX3NhbXBsZXNgOiBUaGUgc2FtcGxlIHNpemUgb2YgZmlzaCAobnVtYmVyIG9mIGxhcmdlbW91dGggYmFzcykgdGFrZW4gZnJvbSB0aGUgbGFrZSB0byBhY3F1aXJlIHRoZSBtZXJjdXJ5IHN0YXRpc3RpY3MgXCoNCiAgKiBgYWdlX2RhdGFgOiBjb250ZXh0IHN1cHBsaWVzIHRoaXMgYXMgdGhlICJhZ2UiIGluIHllYXJzLCBJIHdvdWxkIGFzc3VtZSB0aGUgdHlwaWNhbCBhZ2UgaW4geWVhcnMgb2YgYmFzcyBpbiB0aGUgc2FtcGxlIGZyb20gdGhhdCBsYWtlLiBIb3dldmVyLCBpdCBzZWVtcyBvZGQgdGhhdCB0aGF0IHNvIG1hbnkgZmlzaCB3b3VsZCBiZSBzbyB5b3VuZyB3aGVuLCBhcHBhcmVudGx5LCB0aGVpciBhdmVyYWdlIGxpZmVzcGFuIGlzIFsxNiB5ZWFyc10oaHR0cHM6Ly90cHdkLnRleGFzLmdvdi9odW50d2lsZC93aWxkL3NwZWNpZXMvbG1iLykuIFRoaXMgdmFyaWFibGUgb25seSB0YWtlcyBvbiBiaW5hcnkgdmFsdWVzIG9mIDAgYW5kIDEsIHdoaWNoIHVzdWFsbHkgaW1wbGllcyBhIGxvZ2ljYWwgaW5kaWNhdG9yLiBJIHN1c3BlY3QgdGhpcyB2YXJpYWJsZSBpcyBhIHRydWUtZmFsc2UgaW5kaWNhdG9yIG9mIHdoZXRoZXIgZGF0YSBvbiB0aGUgZmlzaGVzJyBhZ2Ugd2FzIGNvbGxlY3RlZCBmb3IgYSBnaXZlbiBsYWtlLiBPciwgcGVyaGFwcyB0aGlzIDAtMSBkaXN0aW5jdGlvbiBjb3VsZCBiZSBhIHVuaXRsZXNzIHlvdW5nLW9sZCBkZXNpZ25hdGlvbi4gSW4gYW55IGNhc2UsIHRoZSBleGFjdCBtZWFuaW5nIGFuZCB1bml0cyBvZiB0aGlzIHZhcmlhYmxlIGFyZSB1bmNsZWFyIFwqIA0KICANCk91ciBnb2FsIGluIGV4YW1pbmluZyB0aGlzIGRhdGEgaXMgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgdGhlIGFsa2FsaW5pdHkgbGV2ZWxzIG9mIGEgbGFrZSBtaWdodCBpbXBhY3QgTWVyY3VyeSBsZXZlbHMgaW4gbGFyZ2Vtb3V0aCBiYXNzLiBJIGFtIHVuc3VyZSBvZiB0aGUgY29uZGl0aW9ucyB1bmRlciB3aGljaCB0aGlzIGRhdGEgd2VyZSBjb2xsZWN0ZWQsIHRoZSBsYWtlcyBpbiB0aGlzIGRhdGEgbWF5IG9yIG1heSBub3QgYmUgYSByZXByZXNlbnRhdGl2ZSBzYW1wbGUgb2YgYWxsIGxha2VzIGluIGNlbnRyYWwgRmxvcmlkYSwgYW5kIHRoZXkgYXJlIG1vc3QgbGlrZWx5IG5vdCBhIHJlcHJlc2VudGF0aXZlIHNhbXBsZSBvZiBsYWtlcyBpbiBnZW5lcmFsLiBGb3IgdGhlIHNha2Ugb2YgdGhpcyBzdHVkeSwgd2Ugd2lsbCBhc3N1bWUgdGhlIHNhbXBsZSBvZiBsYXJnZW1vdXRoIGJhc3MgZ2F0aGVyZWQgZnJvbSBlYWNoIHJlc3BlY3RpdmUgbGFrZSBpcyBhIHJlcHJlc2VudGF0aXZlIHNhbXBsZSBvZiBhbGwgbGFyZ2Vtb3V0aCBiYXNzIGluIHRoYXQgbGFrZSAoYWx0aG91Z2ggcGVyaGFwcyB0aGlzIGlzIGFuIHVucmVhc29uYWJsZSBhc3N1bXB0aW9uIGZvciBsYWtlcyB3aXRoIHRoZSBzbWFsbGVzdCBzYW1wbGUgc2l6ZXMpLiBJIGhhdmUgbm8gYmFja2dyb3VuZCBpbiBjaGVtaXN0cnkgYnV0IEkgaW1hZ2luZSB0aGUgd2VhdGhlciBvbiB0aGUgZGF5IG9mIG9ic2VydmF0aW9uIGNvdWxkIGltcGFjdCBhbGwgb2YgdGhlIG51bWVyaWMgdmFyaWFibGVzIGluIHRoZSBgYmFzc2AgZGF0YSBzZXQuIENvbnNpZGVyaW5nIGhvdyB3YXRlciBzeXN0ZW1zIGFuZCB3YXRlciBjeWNsZXMgd29yaywgYW5kIHRoZSBmYWN0IHRoYXQgdGhlIHN0dWR5IHdhcyBpbmNpdGVkIGJ5IGluZHVzdHJpYWwgcG9sbHV0aW9uIGNvbmNlcm5zLCB0aGUgbG9jYXRpb24gb2YgdGhlIGxha2VzIHRoZW1zZWx2ZXMgY291bGQgYWxzbyBpbXBhY3Qgc29tZSBvZiB0aGUgdmFyaWFibGVzIGluIHRoaXMgZGF0YSBzZXQuIEJvdGggb2YgdGhlc2UgYXJlIHBvdGVudGlhbGx5IGNvbmZvdW5kaW5nIHZhcmlhYmxlcy4gV2UgY29udGludWUgd2l0aCB0aGUgYW5hbHlzaXMgd2l0aCBhbGwgb2YgdGhpcyBpbiBtaW5kLg0KDQojIyBEaXNjbGFpbWVyDQogIA0KSSBhbSBpbmZlcnJpbmcgdGhlIG1lYW5pbmcgb2YgdGhlc2Ugc3RhcnJlZCB2YXJpYWJsZXMgYXMgbXVjaCBhcyBJIGNhbiBiYXNlZCBvbiB0aGVpciBuYW1lcywgdGhlIGZvY3VzIG9mIHRoZSBzdHVkeSwgYW5kIHRoZSBjb250ZXh0IG9mIHRoZSBzY2VuYXJpbyBnaXZlbi4gSSBhbSBvbmx5IGFzc3VtaW5nIHRoaXMgaXMgdGhlaXIgdHJ1ZSBtZWFuaW5nLiANCiAgDQpJIGluaXRpYWxseSB0aG91Z2h0IGBJRGAgd2FzIG1lYW5pbmdsZXNzIG90aGVyIHRoYW4gc2VydmluZyBhcyBhbiBpZGVudGlmaWVyLiBJIGFzc3VtZWQgdGhhdCB0aGUgbGFrZXMgd2VyZSBhcnJhbmdlZCBpbiB0aGUgZGF0YSBzZXQgYW5kIHRoZW4gYW4gSUQgd2FzIGFzc2lnbmVkIGFjY29yZGluZyB0byB0aGUgbGFrZSdzIHJvdyBudW1iZXIuIEJ1dCBldmVudHVhbGx5IEkgbm90aWNlZCB0aGF0IG5vdCBldmVyeSBsYWtlJ3MgSUQgY29ycmVzcG9uZHMgdG8gdGhlaXIgcm93IG51bWJlciwgc3BlY2lmaWNhbGx5IHRoZSBsYWtlcyB3aXRoIElEcyBiZXR3ZWVuIDQ3IGFuZCA1MSBpbmNsdXNpdmUuIFRoaXMgY291bGQganVzdCBiZSBhIHNjcmlwdGluZyBtaXN0YWtlLCBpdCBkb2VzIHNlZW0gbGlrZSBsYWtlIDUxIHdhcyBqdXN0IG1vdmVkIHVwIHRvIHJvdyA0NyBhbmQgZXZlcnl0aGluZyB3YXMgZGlzam9pbnRlZCBmcm9tIHRoZXJlLiBCdXQgdGhlcmUgaXMgc3RpbGwgdGhlIHBvc3NpYmlsaXR5IHRoYXQgYElEYCBoYXMgc29tZSBtZWFuaW5nLg0KDQpgYGB7cn0NCnRhaWwoYmFzcywxMCkNCmBgYA0KDQoNCkkgd2FzIGluaXRpYWxseSB1bnN1cmUgb2Ygd2hhdCBgbWluYCBhbmQgYG1heGAgcmVmZXJyZWQgdG8sIGJ1dCBsb29raW5nIGF0IGhvdyBlYWNoIGxha2UncyB2YWx1ZXMgZm9yIHRoZXNlIHZhcmlhYmxlcyBhcmUgc28gY2xvc2VseSBzeW5jaHJvbml6ZWQgd2l0aCB0aGUgbGFrZXMnIHZhbHVlcyBmb3IgYE1lcmN1cnlgIGxlZCBtZSB0aGUgdGhlIGFmb3JlbWVudGlvbmVkIGludGVycHJldGF0aW9uLiBUaGUgcGxvdCBiZWxvdyBkaXNwbGF5cyB0aGlzLiBUaGUgb3ZlcmFsbCB0cmVuZCBpcyB1bmltcG9ydGFudCwgYnV0IG5vdGljZSB0aGF0IHZhbHVlcyBvZiBgTWVyY3VyeWAgKHJlZCBsaW5lKSB0ZW5kIHRvIGJlIGFzc29jaWF0ZWQgd2l0aCBzaW1pbGFyIHZhbHVlcyBvZiBgbWF4YCAoYmx1ZSBsaW5lKSBhbmQgYG1pbmAgKGdyZWVuIGxpbmUpLiBUaGlzIHBhdHRlcm4gd2FzIG5vdCBwcmV2YWxlbnQgd2hlbiByZXBsYWNpbmcgYE1lcmN1cnlgIHdpdGggYW55IG90aGVyIHZhcmlhYmxlIGluIHRoZSBkYXRhLiBUaGUgdXNlIG9mIGEgbGluZSBwbG90IGhlcmUgaXNuJ3QgbmVjZXNzYXJpbHkgYXBwcm9wcmlhdGUgc2luY2UgYElEYCBpcyB0cnVseSBjYXRlZ29yaWNhbCwgc28gdGhlIGNvcnJlc3BvbmRpbmcgYmFyIGNoYXJ0IGlzIGFsc28gc2hvd24sIGFsdGhvdWdoIHRoZSBwYXR0ZXJuIGlzbid0IGFzIGFwcGFyZW50Lg0KDQpgYGB7cn0gDQojIExpbmUgY2hhcnQNCmxpbmVzIDwtIGdncGxvdChiYXNzKSArDQogICAgIGdlb21fbGluZShhZXMoeD1JRCx5PW1pbiksIGNvbG9yPSJncmVlbiIsIGx3ZD0xLGFscGhhPTAuNikrDQogICAgIGdlb21fbGluZShhZXMoeD1JRCx5PU1lcmN1cnkpLCBjb2xvcj0icmVkIiwgbHdkPTEsYWxwaGE9MC42KSsNCiAgICAgZ2VvbV9saW5lKGFlcyh4PUlELHk9bWF4KSwgY29sb3I9ImJsdWUiLCBsd2Q9MSxhbHBoYT0wLjYpICsgeWxhYigiIikNCiAgICAgDQojIEJhciBjaGFydA0KYmFycyA8LSBnZ3Bsb3QoYmFzcykgKw0KICBnZW9tX2NvbChhZXMoeD1JRCx5PW1heCksIGZpbGw9ImJsdWUiLGFscGhhPTAuNikrICAgDQogIGdlb21fY29sKGFlcyh4PUlELHk9TWVyY3VyeSksIGZpbGw9InJlZCIsYWxwaGE9MC42KSsgDQogIGdlb21fY29sKGFlcyh4PUlELHk9bWluKSwgZmlsbD0iZ3JlZW4iLCBhbHBoYT0wLjYpICsgeWxhYigiIikNCiAgICAgDQpncmlkLmFycmFuZ2UobGluZXMsYmFycyxuY29sPTEpDQpgYGANCiAgDQojIyBFeGFtaW5pbmcgdGhlIERhdGEgIA0KICANCkhlcmUgaXMgYSBicmllZiBsb29rIGF0IHRoZSBkYXRhLg0KYGBge3J9DQpoZWFkKGJhc3MpDQpgYGANCg0KDQpCZWxvdyBpcyBhIHRhYmxlIG9mIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgdGhlIGBiYXNzYCBkYXRhIHNldC4NCmBgYHtyfQ0KIyBEaXNwbGF5IHN1bW1hcnkgc3RhdGlzdGljcyBvZiB0aGUgZGF0YQ0Kc2tpbShiYXNzKQ0KYGBgDQogIA0KSGVyZSB3ZSBjcmVhdGUgaGlzdG9ncmFtcyBmb3IgdGhlIG1lYW5pbmdmdWwgbnVtZXJpYyB2YXJpYWJsZXMgKGkuZS4gbmVpdGhlciBgSURgIG5vciBgTGFrZWApLCB3aGljaCB3aWxsIGJlIHNob3duIGFmdGVyIHRoZSB0ZXh0dWFsIGRlc2NyaXB0aW9uIHVuZGVyIHRoaXMgY29kZS4NCg0KYGBge3J9DQojIENyZWF0ZSBoaXN0aWdyYW1zIGZvciBlYWNoIG1lYW5pbmdmdWwgbnVtZXJpYyB2YXJpYWJsZQ0KIyBIaXN0b2dyYW1zIHdpbGwgYmUgZGlzcGxheWVkIGJlbG93IHN1bW1hcnkgKHRoZSB0ZXh0IHVuZGVyIHRoaXMgY29kZSBjaHVuaykNCg0KaGlzdEFsayA8LSBnZ3Bsb3QoYmFzcywgYWVzKHg9QWxrYWxpbml0eSkpICsgDQogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4pLCBjb2xvcj0iZ3JleSIsZmlsbD0id2hpdGUiLCBiaW5zPTIwKSsNCiAgICBnZW9tX2RlbnNpdHkoYWxwaGE9MCwgZmlsbD0id2hpdGUiLGNvbG9yPSJyZWQiLGx3ZD0xKSsNCiAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PW1lYW4oQWxrYWxpbml0eSkpLGNvbG9yPSJibHVlIiwgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MSkNCg0KaGlzdHBIIDwtIGdncGxvdChiYXNzLCBhZXMoeD1wSCkpICsgDQogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4pLCBjb2xvcj0iZ3JleSIsZmlsbD0id2hpdGUiLCBiaW5zPTIwKSsNCiAgICBnZW9tX2RlbnNpdHkoYWxwaGE9MCwgZmlsbD0id2hpdGUiLGNvbG9yPSJyZWQiLGx3ZD0xKSArDQogICAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1tZWFuKHBIKSksY29sb3I9ImJsdWUiLCBsaW5ldHlwZT0iZGFzaGVkIiwgc2l6ZT0xKQ0KICANCg0KaGlzdENhbCA8LSBnZ3Bsb3QoYmFzcywgYWVzKHg9Q2FsY2l1bSkpICsgDQogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4pLCBjb2xvcj0iZ3JleSIsZmlsbD0id2hpdGUiLCBiaW5zPTIwKSsNCiAgICBnZW9tX2RlbnNpdHkoYWxwaGE9MCwgZmlsbD0id2hpdGUiLGNvbG9yPSJyZWQiLGx3ZD0xKSsNCiAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PW1lYW4oQ2FsY2l1bSkpLGNvbG9yPSJibHVlIiwgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MSkNCg0KaGlzdENobCA8LSBnZ3Bsb3QoYmFzcywgYWVzKHg9Q2hsb3JvcGh5bGwpKSArIA0KICAgIGdlb21faGlzdG9ncmFtKGFlcyh5PS4uZGVuc2l0eS4uKSwgY29sb3I9ImdyZXkiLGZpbGw9IndoaXRlIiwgYmlucz0yMCkrDQogICAgZ2VvbV9kZW5zaXR5KGFscGhhPTAsIGZpbGw9IndoaXRlIixjb2xvcj0icmVkIixsd2Q9MSkgKw0KICAgIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9bWVhbihDaGxvcm9waHlsbCkpLGNvbG9yPSJibHVlIiwgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MSkNCg0KaGlzdE5TIDwtIGdncGxvdChiYXNzLCBhZXMoeD1Ob19zYW1wbGVzKSkgKyANCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeT0uLmRlbnNpdHkuLiksIGNvbG9yPSJncmV5IixmaWxsPSJ3aGl0ZSIsIGJpbnM9MjApKw0KICAgIGdlb21fZGVuc2l0eShhbHBoYT0wLCBmaWxsPSJ3aGl0ZSIsY29sb3I9InJlZCIsbHdkPTEpICsgeGxhYigiU2FtcGxlIFNpemUiKSArDQogICAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1tZWFuKE5vX3NhbXBsZXMpKSxjb2xvcj0iYmx1ZSIsIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTEpDQoNCmhpc3RBZ2UgPC0gZ2dwbG90KGJhc3MsIGFlcyh4PWFnZV9kYXRhKSkgKyANCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeT0uLmRlbnNpdHkuLiksIGNvbG9yPSJncmV5IixmaWxsPSJ3aGl0ZSIsIGJpbnM9MjApKw0KICAgIGdlb21fZGVuc2l0eShhbHBoYT0wLCBmaWxsPSJ3aGl0ZSIsY29sb3I9InJlZCIsbHdkPTEpICsNCiAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PW1lYW4oYWdlX2RhdGEpKSxjb2xvcj0iYmx1ZSIsIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTEpDQoNCmhpc3RNaW4gPC0gZ2dwbG90KGJhc3MsIGFlcyh4PW1pbikpICsgDQogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4pLCBjb2xvcj0iZ3JleSIsZmlsbD0id2hpdGUiLCBiaW5zPTIwKSsNCiAgICBnZW9tX2RlbnNpdHkoYWxwaGE9MCwgZmlsbD0id2hpdGUiLGNvbG9yPSJyZWQiLGx3ZD0xKSArIHhsYWIoIiBNaW4gTWVyY3VyeSIpICsNCiAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PW1lYW4obWluKSksY29sb3I9ImJsdWUiLCBsaW5ldHlwZT0iZGFzaGVkIiwgc2l6ZT0xKQ0KDQpoaXN0TWVyYyA8LSBnZ3Bsb3QoYmFzcywgYWVzKHg9TWVyY3VyeSkpICsgDQogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4pLCBjb2xvcj0iZ3JleSIsZmlsbD0id2hpdGUiLCBiaW5zPTIwKSsNCiAgICBnZW9tX2RlbnNpdHkoYWxwaGE9MCwgZmlsbD0id2hpdGUiLGNvbG9yPSJyZWQiLGx3ZD0xKSArIHhsYWIoIiBUeXBpY2FsIE1lcmN1cnkiKSArDQogICAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1tZWFuKE1lcmN1cnkpKSxjb2xvcj0iYmx1ZSIsIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTEpDQoNCmhpc3RNYXggPC0gZ2dwbG90KGJhc3MsIGFlcyh4PW1heCkpICsgDQogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4pLCBjb2xvcj0iZ3JleSIsZmlsbD0id2hpdGUiLCBiaW5zPTIwKSsNCiAgICBnZW9tX2RlbnNpdHkoYWxwaGE9MCwgZmlsbD0id2hpdGUiLGNvbG9yPSJyZWQiLGx3ZD0xKSsgeGxhYigiIE1heCBNZXJjdXJ5IikgKw0KICAgIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9bWVhbihtYXgpKSxjb2xvcj0iYmx1ZSIsIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTEpDQpgYGANCg0KICAqIGBBbGthbGluaXR5YCBpcyBoZWF2aWx5IHBvc2l0aXZlbHkgc2tld2VkIHdpdGggYSBtZWFuIG9mIDM3LjUzIG1nL0wsIGEgbWVkaWFuIG9mIDE5LjYgbWcvTCwgYW5kIGEgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIDM4LjIgbWcvTA0KICAqIGBwSGAgaXMgcm91Z2hseSBzeW1tZXRyaWMgYW5kIGJlbGwgc2hhcGVkIHdpdGggYSBzbGlnaHQgbmVnYXRpdmUgc2tldy4gVGhlIG1lYW4gcEggbGV2ZWwgb2YgbGFrZXMgaW4gdGhpcyBkYXRhIGlzIDYuNTkgd2l0aCBhIHN0YW5kYXJkIGRldmlhdGlvbiBvZiAxLjI5LiBUaGUgbWVkaWFuIHBIIGxldmVsIG9mIHRoZSBsYWtlcyBpcyA2LjguDQogICogYENhbGNpdW1gIGhhcyBhIGhlYXZ5IHBvc2l0aXZlIHNrZXcgd2l0aCBhIG1lYW4gY29uY2VudHJhdGlvbiBvZiAyMi4yIG1nL0wsIGEgbWVkaWFuIG9mIDEyLjYgbWcvTCwgYW5kIGEgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIDI0LjkzIG1nL0wNCiAgKiBgQ2hsb3JvcGh5bGxgIGhhcyBhIGhlYXZ5IHBvc2l0aXZlIHNrZXcsIGEgbWVhbiBvZiAyMy4xMiBtY2cvTCwgYSBtZWRpYW4gb2YgMTIuOCBtY2cvTCwgYW5kIGEgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIDMwLjgyIG1jZy9MDQogICogVGhlIHNpemUgb2YgdGhlIGJhc3Mgc2FtcGxlcyBmb3IgdGhlIGxha2VzLCBgTm9fc2FtcGxlYCwgaXMgaGVhdmlseSBwb3NpdGl2ZWx5IHNrZXdlZCB3aXRoIGEgbWVhbiBvZiAxMy4wNiBiYXNzLCBhIG1lZGlhbiBvZiAxMiBiYXNzLCBhbmQgYSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgOC41NiBiYXNzICANCiAgKiBgYWdlX2RhdGFgIHNlZW1zIHRvIGZvbGxvdyBhIEJlcm5vdWxsaSBkaXN0cmlidXRpb24sIGFsbCB2YWx1ZXMgYXJlIGVpdGhlciAwIG9yIDEuIFRoZSBtZWFuLCBtZWRpYW4sIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gYXJlIDAuODEsIDEsIGFuZCAwLjM5IHJlc3BlY3RpdmVseSwgYWx0aG91Z2ggdGhlc2UgdmFsdWVzIGFyZW4ndCBtZWFuaW5nZnVsIHNpbmNlIHdlIGFyZSB1bnN1cmUgb2YgdGhlIHVuaXRzIG9yIGNvbnRleHQgYmVoaW5kIHRoaXMgZGF0YS4gfjIwJSBvZiB0aGUgbGFrZXMgaGF2ZSBhIHZhbHVlIG9mIDAgZm9yIGBhZ2VfZGF0YWAsIH43OSUgKHRoZSByZXN0KSBvZiB0aGUgbGFrZXMgaGF2ZSBhIHZhbHVlIG9mIDEgKHJvdW5kaW5nIGVycm9yLCB0aGVyZSBpcyBubyBtaXNzaW5nIGRhdGEpLg0KICAqICBUaGUgbWluaW11bSBNZXJjdXJ5IGNvbmNlbnRyYXRpb24gaW4gYmFzcyBtdXNjbGUgdGlzc3VlLCBgbWluYCAsIGlzIHBvc2l0aXZlbHkgc2tld2VkIHdpdGggYSBtZWFuIG9mIDAuMjggbWNnLCBhIG1lZGlhbiBvZiAwLjI1IG1jZywgYW5kIGEgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIDAuMjMgbWNnIA0KICAqIFRoZSB0eXBpY2FsIE1lcmN1cnkgY29uY2VudHJhdGlvbiBpbiBiYXNzIG11c2NsZSB0aXNzdWUsIGBNZXJjdXJ5YCAsIGlzIHBvc2l0aXZlbHkgc2tld2VkIHdpdGggYSBtZWFuIG9mIDAuNTEgbWNnLCBhIG1lZGlhbiBvZiAwLjQ1IG1jZywgYW5kIGEgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIDAuMzQgbWNnDQogICogYG1heGA6IFRoZSBtYXhpbXVtIE1lcmN1cnkgY29uY2VudHJhdGlvbiBpbiBiYXNzIG11c2NsZSB0aXNzdWUsIGBtYXhgICwgaXMgc2xpZ2h0bHkgcG9zaXRpdmVseSBza2V3ZWQgd2l0aCBhIG1lYW4gb2YgMC44NyBtY2csIGEgbWVkaWFuIG9mIDAuNDUgbWNnLCBhbmQgYSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgMC4zNCBtY2cNCiAgDQogIA0KVGhlIGRpc3RyaWJ1dGlvbnMgZm9yIHRoZXNlIHZhcmlhYmxlcyBhbmQgdGhlaXIgcmVzcGVjdGl2ZSBtZWFucyBhcmUgc2hvd24gYmVsb3cuDQoNCmBgYHtyfSANCiMgRGlzcGxheSBoaXN0b2dyYW1zIGluIGEgMyBieSAzIG1hdHJpeA0KZ3JpZC5hcnJhbmdlKGhpc3RBbGssaGlzdHBILGhpc3RDYWwsaGlzdENobCxoaXN0TlMsaGlzdEFnZSxoaXN0TWluLGhpc3RNZXJjLGhpc3RNYXgsIG5jb2w9MykNCmBgYA0KICANClRoZSBvbmx5IGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBhcmUgYElEYCBhbmQgYExha2VgLCBib3RoIG9mIHdoaWNoIGFyZSB1bmlxdWUgaWRlbnRpZnlpbmcgdmFsdWVzIGZvciB0aGUgbGFrZSB0aGF0IHRoZXkgY29ycmVzcG9uZCB0by4gRXZlbiB0aG91Z2ggYElEYCBpcyByZWNvcmRlZCBhcyBhIG51bWJlciwgaXQgaXMgY2F0ZWdvcmljYWwgaW4gbmF0dXJlLg0KICANClRoZXJlIGlzIG5vIG1pc3NpbmduZXNzIGluIG91ciBkYXRhLg0KYGBge3J9DQp0YWJsZShpcy5uYShiYXNzKSkNCmBgYA0KICANCiMgUTMgLSBQbG90dGluZyB0aGUgRGF0YQ0KICANCkRvIGFsa2FsaW5pdHkgbGV2ZWxzIG9mIGEgbGFrZSBpbXBhY3QgTWVyY3VyeSBsZXZlbHMgaW4gbGFyZ2Vtb3V0aCBiYXNzPyBUbyBpbnZlc3RpZ2F0ZSB0aGlzIHJlc2VhcmNoIHF1ZXN0aW9uIHdlIGV4YW1pbmUgdGhlIGFsa2FsaW5pdHkgb2YgbGFrZXMgaW4gY2VudHJhbCBGbG9yaWRhLCBgQWxrYWxpbml0eWAsIGFzIGEgcHJlZGljdG9yIG9mIHRoZSB0eXBpY2FsIGFtb3VudCBvZiBNZXJjdXJ5IGluIGEgbGFyZ2Vtb3V0aCBiYXNzIGZyb20gdGhlIGxha2UsIGBNZXJjdXJ5YC4NCg0KYGBge3J9IA0KIyBDcmVhdGUgc2NhdHRlcnBsb3Qgb2YgTWVyY3VyeX5BbGthbGluaXR5DQpzY2F0dGVyPC0NCiAgZ2dwbG90KGJhc3MsIGFlcyh4PUFsa2FsaW5pdHkseT1NZXJjdXJ5KSkgKw0KICBnZW9tX3BvaW50KGRhdGE9YmFzcywgc2l6ZT0zLGNvbG9yPSJibGFjayIsZmlsbD0iYmx1ZSIsc2hhcGU9MjEpKw0KICBnZW9tX3BvaW50KGRhdGE9YmFzc1tiYXNzJExha2U9PSJQdXp6bGUgIixdLHNpemU9Myxjb2xvcj0iYmxhY2siLGZpbGw9ImdyZWVuIixzaGFwZT0yMSkrICMgQ29sb3Igb3V0bGllciBncmVlbg0KICB4bGFiKCJMYWtlIEFsa2FsaW5pdHkgKG1nL0wpIikgKw0KICB5bGFiKCJUeXBpY2FsIEFtb3VudCBvZiBNZXJjdXJ5IGluIEJhc3MgKG1jZykiKSArDQogIGdndGl0bGUoIk1lcmN1cnkgQ29udGVudCB2ZXJzdXMgTGFrZSBBbGthbGluaXR5IikNCg0KIyBTaG93IGludGVyYWN0aXZlIHBsb3QNCmdncGxvdGx5KHNjYXR0ZXIpDQpgYGANCg0KQWJvdmUgaXMgYSBwbG90IG9mIGxha2UgYWxrYWxpbml0eSBpbiBtaWxsaWdyYW1zIHBlciBMaXRlciBhcyBhIHByZWRpY3RvciBvZiB0aGUgbWVyY3VyeSBjb250ZW50IGluIG1pY3JvZ3JhbXMgZm9yIGxhcmdlbW91dGggYmFzcyBmcm9tIHRoZSBsYWtlLCBgTWVyY3VyeX5BbGthbGluaXR5YC4gRWFjaCBwb2ludCBpbiB0aGUgcGxvdCBhYm92ZSByZXByZXNlbnRzIG9uZSByb3cgZnJvbSB0aGUgYGJhc3NgIGRhdGEgc2V0LCBkYXRhIGZyb20gb25lIGxha2UgaW4gY2VudHJhbCBGbG9yaWRhLiBUaGUgZ3JlZW4gcG9pbnQgcmVwcmVzZW50cyBsYWtlIDQwIG9yIExha2UgIlB1enpsZSIsIGEgbGlrZWx5IG91dGxpZXIgbGFrZSB3aXRoIGJhc3Mgb2YgdW51c3VhbGx5IGhpZ2ggbWVyY3VyeSBjb250ZW50IGNvbnNpZGVyaW5nIHRoZSBsYWtlJ3MgYWxrYWxpbml0eS4gVGhlcmUgYXJlIG5vIG90aGVyIHVudXN1YWwgcG9pbnRzLiAgDQogIA0KVGhlcmUgaXMgYSBtb2RlcmF0ZWx5IHN0cm9uZywgbmVnYXRpdmUgYXNzb2NpYXRpb24gYmV0d2VlbiBgTWVyY3VyeWAgYW5kIGBBbGthbGluaXR5YCBidXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB0d28gaXMgY2xlYXJseSBub3QgbGluZWFyLiBBcyBpcywgaXQgd291bGQgbm90IGJlIGFwcHJvcHJpYXRlIHRvIHBlcmZvcm0gYSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24uIEluIGFkZGl0aW9uIHRvIGEgY2xlYXIgdmlvbGF0aW9uIG9mIGNvbmRpdGlvbiB0aGF0IHRoZSB0cmVuZCBpbiB0aGUgZGF0YSBiZSBMaW5lYXIsIHRoZSBub24tbGluZWFyaXR5IGlzIHN1Y2ggYSBkb21pbmF0aW5nIHRyYWl0IG9mIHRoZSBwbG90IHRoYXQgdGhlIG90aGVyIGNvbmRpdGlvbnMgY2Fubm90IGJlIGFjY3VyYXRlbHkgYXNzZXNzZWQuIEhvd2V2ZXIsIHRyYW5zZm9ybWluZyB0aGUgZGF0YSBtYXkgbWFrZSBpdCBhcHByb3ByaWF0ZSBmb3Igc3VjaCBhIG1vZGVsLg0KDQpXZSBlYXJsaWVyIGRpc2N1c3NlZCB0aGUgcG90ZW50aWFsIGZvciBjb25mb3VuZGluZyB2YXJpYWJsZXMgc3VjaCBhcyBnZW9ncmFwaGljYWwgbG9jYXRpb24gb2YgdGhlIGxha2UgYW5kIHRoZSBsb2NhbCB3ZWF0aGVyIG9uIHRoZSBkYXkgb2Ygb2JzZXJ2YXRpb24uIFdlIGFsc28gY29uc2lkZXJlZCB0aGF0IG91ciBkYXRhIG1heSBub3QgYmUgcmVwcmVzZW50YXRpdmUgb2YgYWxsIGxha2VzIGluIEZsb3JpZGEgZGVwZW5kaW5nIG9uIGhvdyB0aGUgc2FtcGxlIG9mIGxha2VzIHdhcyBjaG9zZW4sIGFuZCB0aGF0IG91ciBiYXNzIHNhbXBsZSB3aXRoaW4gb25lIGxha2UgbWF5IG5vdCBiZSByZXByZXNlbnRhdGl2ZSBvZiBhbGwgYmFzcyBpbiB0aGF0IGxha2UgZm9yIHRoZSBlc3BlY2lhbGx5IHNtYWxsIGJhc3Mgc2FtcGxlcyAocm93cyBmb3Igd2hpY2ggYE5vX3NhbXBsZXNgIGlzIHNtYWxsKS4gQW55IHRyZW5kcyB3ZSBkbyBzZWUgbWF5IG5vdCBiZSBhcHBsaWNhYmxlIGJleW9uZCBsYWtlcyBpbiBjZW50cmFsIEZsb3JpZGEuDQogIA0KIyBRNCAtIEluaXRpYWwgTW9kZWwNCiAgDQpIZXJlIHdlIGZpdCB0aGUgU2ltcGxlIExpbmVhciBSZWdyZXNzaW9uIG1vZGVsIGZvciB0aGUgYWxrYWxpbml0eSBvZiBhIGxha2UgaW4gY2VudHJhbCBGbG9yaWRhLCBgQWxrYWxpbml0eWAsIGFzIGEgcHJlZGljdG9yIG9mIHRoZSB0eXBpY2FsIGFtb3VudCBvZiBNZXJjdXJ5IGluIGEgbGFyZ2Vtb3V0aCBiYXNzIGZyb20gdGhlIGxha2UsIGBNZXJjdXJ5YC4gVGhlIG1vZGVsIHN1bW1hcnkgaXMgcHJpbnRlZCBvdXQgYmVsb3cuIEJlbmVhdGggdGhlIG1vZGVsIHN1bW1hcnkgaXMgYSBzY2F0dGVycGxvdCBvZiBgTWVyY3VyeWAgdmVyc3VzIGBBbGthbGluaXR5YCBpbmNsdWRpbmcgdGhlIHJlZ3Jlc3Npb24gbGluZSBhbmQgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgYXJvdW5kIHRoZSByZWdyZXNzaW9uIGxpbmUuDQpgYGB7cn0NCiMgRml0IE1lcmN1cnl+QWxrYWxpbml0eSBtb2RlbA0KYmFkLm1vZGVsIDwtIG9sc19yZWdyZXNzKE1lcmN1cnl+QWxrYWxpbml0eSwgZGF0YT1iYXNzKQ0KYmFkLm1vZGVsDQpgYGANCg0KYGBge3J9IA0Kc2NhdHRlci5saW5lIDwtIA0KICBnZ3Bsb3QoYmFzcywgYWVzKHg9QWxrYWxpbml0eSx5PU1lcmN1cnkpKSArDQogIGdlb21fc21vb3RoKGRhdGE9YmFzcyxhZXMoeD1BbGthbGluaXR5LHk9TWVyY3VyeSksIG1ldGhvZD0ibG0iLCBmb3JtdWxhPXl+eCkrICMgcGxvdCByZWdyZXNzaW9uIGxpbmUNCiAgZ2VvbV9wb2ludChkYXRhPWJhc3MsIHNpemU9Myxjb2xvcj0iYmxhY2siLGZpbGw9ImJsdWUiLHNoYXBlPTIxKSsNCiAgZ2VvbV9wb2ludChkYXRhPWJhc3NbYmFzcyRMYWtlPT0iUHV6emxlICIsXSxzaXplPTMsY29sb3I9ImJsYWNrIixmaWxsPSJncmVlbiIsc2hhcGU9MjEpKyAjIENvbG9yIG91dGxpZXIgZ3JlZW4NCiAgeGxhYigiTGFrZSBBbGthbGluaXR5IChtZy9MKSIpICsNCiAgeWxhYigiVHlwaWNhbCBBbW91bnQgb2YgTWVyY3VyeSBpbiBCYXNzIChtY2cpIikgKw0KICBnZ3RpdGxlKCJNZXJjdXJ5IENvbnRlbnQgdmVyc3VzIExha2UgQWxrYWxpbml0eSIpDQogIA0KDQogIA0KZ2dwbG90bHkoc2NhdHRlci5saW5lKQ0KYGBgDQpGcm9tIG91ciBTTFIgbW9kZWwgd2UgZ2V0IHRoZSBmb2xsb3dpbmcgZXF1YXRpb24NCg0KXFtcd2lkZWhhdHtNZXJjdXJ5IENvbnRlbnR9PTAuNzIyKygtMC4wMDYpIFx0aW1lcyBBbGthbGluaXR5XF0NCiAgDQpXaGVyZSAkTWVyY3VyeUNvbnRlbnQkIGlzIHRoZSBhbW91bnQgb2YgbWVyY3VyeSBpbiBtaWNyb2dyYW1zIGluIHRoZSBtdXNjbGUgdGlzc3VlIG9mIGEgdHlwaWNhbCBmaXNoIGZyb20gYSBsYWtlIGFuZCAkQWxrYWxpbml0eSQgaXMgdGhlIEFsa2FsaW5pdHkgKHRoZSBhbW91bnQgb2YgYWNpZCBuZWVkZWQgdG8gYnJpbmcgbGFrZSB3YXRlciB0byBhIHBIIG9mIDQuMikgb2YgYSBsYWtlIGluIG1pbGxpZ3JhbXMgcGVyIExpdGVyLiBJZiBvdXIgU0xSIGFzc3VtcHRpb25zIG9mIExpbmVhcml0eSwgSW5kZXBlbmRlbmNlLCBOb3JtYWwgcmVzaWR1YWxzLCBhbmQgSG9tb3NjZWRhc3RpY2l0eSB3ZXJlIG1ldCwgdGhlbiB3ZSB3b3VsZCBpbnRlcnByZXQgdGhlIG1vZGVsIG91dHB1dCBhcyBzaG93aW5nIGEgc3Ryb25nLCBuZWdhdGl2ZSBsaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIG1lcmN1cnkgY29udGVudCBvZiBhIGZpc2ggYW5kIGxha2UgYWxrYWxpbml0eS4gT3VyIFItc3F1YXJlZCB2YWx1ZSB3b3VsZCBiZSB0ZWxsaW5nIHVzIHRoYXQgYWJvdXQgfjM5LjQlIG9mIHRoZSB2YXJpYXRpb24gaW4gYmFzcyBtZXJjdXJ5IGNvbnRlbnQgaXMgZXhwbGFpbmVkIGJ5IHZhcmlhdGlvbiBpbiBsYWtlIGFsa2FsaW5pdHkuIFdpdGggYSBwLXZhbHVlIG9mIGFwcHJveGltYXRlbHkgMCB3ZSB3b3VsZCBoYXZlIHN0cm9uZyBldmlkZW5jZSB0byBzdWdnZXN0IGEgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGJhc3MgbWVyY3VyeSBjb250ZW50IGFuZCBsYWtlIGFsa2FsaW5pdHkuIEEgbGFrZSB3aXRoIGFsa2FsaW5pdHkgb2YgMCBtZy9MIHdvdWxkIGJlIGV4cGVjdGVkIHRvIGhhdmUgYmFzcyB3aXRoIGEgdHlwaWNhbCBtZXJjdXJ5IGNvbnRlbnQgb2YgMC43MjIgbWNnLiBBbmQgZm9yIGV2ZXJ5IGFkZGl0aW9uYWwgbWcvTCBvZiBhbGthbGluaXR5IGluIGEgbGFrZSB3ZSB3b3VsZCBleHBlY3QgdGhlIHR5cGljYWwgbWVyY3VyeSBjb250ZW50IG9mIHRoZSBmaXNoIHRvIGRlY3JlYXNlIGJ5IGFib3V0IDAuMDA2IG1jZy4gSG93ZXZlciwgd2UgY2FuJ3QgcmVhbGx5IGNvbmNsdWRlIGFueSBvZiB0aGlzIHNpbmNlIG91ciBkYXRhIHZpb2xhdGVzIHRoZSBTTFIgYXNzdW1wdGlvbnMuICANCg0KIyBRNSAtIEV4YW1pbmluZyB0aGUgTW9kZWwNCiAgDQojIyBTTFIgQXNzdW1wdGlvbnMgKEwuSS5OLkUpDQogIA0KSGVyZSB3ZSBhc3Nlc3Mgd2hldGhlciBvdXIgbW9kZWwgbWV0IHRoZSBmb2xsb3dpbmcgU0xSIGFzc3VtcHRpb25zOg0KICANCiAgKiBMaW5lYXIgZGF0YQ0KICAqIEluZGVwZW5kZW50IGVycm9ycw0KICAqIE5vcm1hbGx5IGRpc3RyaWJ1dGVkIGVycm9ycw0KICAqIEhvbW9zY2VkYXN0aWMgZXJyb3JzDQogIA0KVG8gZ2FpbiBpbmZlcmVuY2UgYWJvdXQgdGhlIHRydWUgZXJyb3IgdGVybXMgb2Ygb3VyIHBvcHVsYXRpb24gcmVncmVzc2lvbiBsaW5lLCB3ZSBleGFtaW5lIHRoZSBtb2RlbCByZXNpZHVhbHMuICANCiAgDQpUaGUgUmVzaWR1YWwgdnMgRml0IHBsb3QgY2xlYXJseSBzaG93cyBhIGN1cnZlZCB0cmVuZCBpbiB0aGUgcmVzaWR1YWxzLiBUaGUgbGFjayBvZiBhIHJhbmRvbSBwYXR0ZXJuIGlzIGFub3RoZXIgaW5kaWNhdG9yIHRoYXQgb3VyIGRhdGEgd2FzIG5vdCBsaW5lYXIsIGJ1dCB3ZSBjb3VsZCBhbHNvIHNlZSB0aGlzIGRpcmVjdGx5IGZyb20gb3VyIHNjYXR0ZXJwbG90cyBhYm92ZS4gVGhlIGRhdGEgaXMgKipub3QgbGluZWFyKiouICBJdCBhbHNvIGFwcGVhcnMgdGhhdCB0aGVyZSBpcyBtb3JlIHZhcmlhbmNlIGluIHRoZSByZXNpZHVhbHMgZm9yIGxvd2VyIGZpdHRlZCB2YWx1ZXMgdGhhbiBmb3IgaGlnaGVyIGZpdHRlZCB2YWx1ZXMsIG91ciByZXNpZHVhbHMgaGF2ZSAqKnVuZXF1YWwgdmFyaWFuY2UqKi4gSW4gb3RoZXIgd29yZHMsIG91ciByZXNpZHVhbHMgYXJlIGhldGVyb3NjZWRhc3RpYy4gDQogIA0KYGBge3J9IA0KIyBSZXNpZHVhbCB2cyBGaXQgcGxvdCBpcyBjbGVhcmx5IG5vbi1yYW5kb20NCm9sc19wbG90X3Jlc2lkX2ZpdChiYWQubW9kZWwkbW9kZWwpDQoNCmBgYA0KICANCkJlbG93IHdlIHNlZSBldmlkZW5jZSB0aGF0IG91ciByZXNpZHVhbHMgYXJlICoqbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkKiouIFRoZSBoaXN0b2dyYW0gYW5kIG92ZXJsYWlkIGRlbnNpdHkgcGxvdCBzaG93IHRoYXQgdGhlIHJlc2lkdWFscyBhcmUgcG9zaXRpdmVseSBza2V3ZWQuIElmIG91ciByZXNpZHVhbHMgd2VyZSBub3JtYWxseSBkaXN0cmlidXRlZCB0aGVuIHRoZSBxdWFudGlsZXMgb2Ygb3VyIHJlc2lkdWFscyB3b3VsZCBjbG9zZWx5IGZvbGxvdyB0aGUgcXVhbnRpbGVzIG9mIGEgdGhlb3JldGljYWwgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgYSByZWxhdGlvbnNoaXAgc2hvd24gYnkgdGhlIHJlZCBsaW5lIGluIHRoZSBRdWFudGlsZS1RdWFudGlsZSAoUVEpIHBsb3QuIEJ1dCBvdXIgUVEgcGxvdCBpcyBjbGVhcmx5IG5vbi1saW5lYXIsIHNob3dpbmcgdGhhdCBvdXIgcmVzaWR1YWxzIGRvIG5vdCBmb2xsb3cgYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBUaGlzIGlzIHJlYWZmaXJtZWQgYnkgdGhlIHJlc3VsdHMgb2YgaHlwb3RoZXNpcyB0ZXN0aW5nLiBBbGwgb2YgdGhlIGh5cG90aGVzaXMgdGVzdHMgYmVsb3cgdXNlIHRoZSBmb2xsb3dpbmcgaHlwb3RoZXNlczoNCg0KICAqICRIXzAkOiBUaGUgcmVzaWR1YWxzIGFyZSBub3JtYWxseSBkaXN0cmlidXRlZA0KICAqICRIX0EkOiBUaGUgcmVzaWR1YWxzIGFyZSBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQNCiAgDQpUaGUgbWFqb3JpdHkgY29uc2Vuc3VzIG9mIHNldmVyYWwgaHlwb3RoZXNpcyB0ZXN0cyBmb3Igbm9ybWFsaXR5IGlzIHRvIHRoZSByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcy4gV2l0aCAzIG91dCBvZiA0IG9mIHRoZSBwLXZhbHVlcyBiZWluZyBhcHByb3hpbWF0ZWx5IDAsIHdlIGhhdmUgZXZpZGVuY2UgdG8gc3VnZ2VzdCB0aGF0IHRoZSBkaXN0cmlidXRpb24gb2Ygb3VyIG1vZGVsJ3MgcmVzaWR1YWxzIGFyZSBub24tbm9ybWFsLiBJZiBvdXIgZXJyb3IgdGVybXMgd2VyZSB0cnVseSBub3JtYWxseSBkaXN0cmlidXRlZCwgaXQgaXMgZXh0cmVtZWx5IHVubGlrZWx5IHRoYXQgd2Ugd291bGQgZ2V0IHJlc2lkdWFscyB3aXRoIHRoaXMgZGlzdHJpYnV0aW9uLg0KDQpgYGB7cn0gDQojIEhpc3RvZ3JhbSBvZiByZXNpZHVhbHMNCm9sc19wbG90X3Jlc2lkX2hpc3QoYmFkLm1vZGVsJG1vZGVsKQ0KDQojIE5vcm1hbCBwcm9iYWJpbGl0eSBwbG90IG9mIHJlc2lkdWFscw0Kb2xzX3Bsb3RfcmVzaWRfcXEoYmFkLm1vZGVsJG1vZGVsKQ0KDQojIEh5cG90aGVzaXMgdGVzdGluZyBmb3IgTm9ybWFsbHkgRGlzdHJpYnV0ZWQgUmVzaWR1YWxzDQpvbHNfdGVzdF9ub3JtYWxpdHkoYmFkLm1vZGVsJG1vZGVsKQ0KYGBgDQogIA0KSWYgd2UgaGFkIGluZm9ybWF0aW9uIG9uIHRoZSB0aW1lcyBhbmQgbG9jYXRpb25zIG9mIHRoZXNlIG9ic2VydmF0aW9ucyB0aGVuIGl0IHNlZW1zIGl0IHdvdWxkIGJlIHdvcnRoIGludmVzdGlnYXRpbmcgc2VyaWFsIGFuZCBzcGF0aWFsIGNvcnJlbGF0aW9ucyBqdXN0IGluIGNhc2UgdGhlIGxha2VzIGFyZSBub3QgaW5kZXBlbmRlbnQuIEluIHRoZSBhYnNlbmNlIG9mIHRoaXMgaW5mb3JtYXRpb24sIHdlIGNhbiBleGFtaW5lIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gZXhpc3RpbmcgdmFyaWFibGVzIGZvciBjb25mb3VuZGluZy4gIA0KDQpCZWxvdyBpcyBhIG1hdHJpeCBzaG93aW5nIHRoZSBwYWlyd2lzZSBzY2F0dGVycGxvdCBhbmQgcGFpcndpc2UgY29ycmVsYXRpb24gYmV0d2VlbiBldmVyeSBudW1lcmljIHZhcmlhYmxlIGluIHRoZSBkYXRhIHNldC4gVW5pdmFyaWF0ZSBkZW5zaXR5IHBsb3RzIGFyZSBkaXNwbGF5ZWQgYWxvbmcgdGhlIGRpYWdvbmFsLg0KDQpgSURgIHNlZW1zIHRvIGhhdmUgbm8gcmVsYXRpb25zaGlwIHdpdGggYW55IG9mIHRoZSBvdGhlciB2YXJpYWJsZXMsIHdoaWNoIHN1cHBvcnRzIHRoZSBpbnRlcnByZXRhdGlvbiB0aGF0IGl0IGlzIGEgbWVhbmluZ2xlc3MgdmFyaWFibGUgb3RoZXIgdGhhbiBhcyBhIHVuaXF1ZSBpZGVudGlmaWVyLg0KDQpBcyBleHBlY3RlZCwgYG1heGAsIGBtaW5gLCBhbmQgYE1lcmN1cnlgIGFyZSBhbGwgaGlnaGx5IGNvcnJlbGF0ZWQgd2l0aCBlYWNoIG90aGVyLiBUaGV5IGFsbCBoYXZlIHNpZ25pZmljYW50IGNvcnJlbGF0aW9ucyB3aXRoIGVhY2ggb3RoZXIsIHNpZ25pZmllZCBieSB0aGUgYXN0ZXJpc2tzLCBhbmQgdGhlaXIgcGxvdHMgc2hvdyBhIGNsZWFyIGxpbmVhciB0cmVuZC4gQWxzbyBub3RlIHRoYXQgYW55IG90aGVyIHZhcmlhYmxlIHdpdGggYW4gYXNzb2NpYXRpb24gd2l0aCBgTWVyY3VyeWAgaGFzIHRoZSBzYW1lIGFzc29jaWF0aW9uIHdpdGggYG1pbmAgYW5kIGBtYXhgLCBhcyBzZWVuIGJ5IHRoZSBzaW1pbGFyaXR5IG9mIGFsbCBvZiB0aGUgcGxvdHMgaW4gYG1pbmAgLCBgbWF4YCAsIGFuZCAgYE1lcmN1cnlgICByb3dzLiBBZ2FpbiwgdGhpcyB3YXMgZXhwZWN0ZWQuIFRoaXMgaXMgZXZpZGVuY2UgdGhhdCBvdXIgaW5pdGlhbCBpbnRlcnByZXRhdGlvbiBvZiB0aGUgYG1pbmAgYW5kIGBtYXhgIHZhcmlhYmxlcyB3YXMgY29ycmVjdC4NCg0KV2UgYWxzbyBzZWUgYSB0cmVuZCBpbiB0aGUgc2NhdHRlcnBsb3Qgb2YgYHBIYCB2ZXJzdXMgYEFsa2FsaW5pdHlgIGFuZCBhIHNpZ25pZmljYW50IGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHR3bywgd2hpY2ggaXMgYWxzbyB0byBiZSBleHBlY3RlZCBiZWNhdXNlIHBIIG1lYXN1cmVzIGhvdyBhbGthbGluZSBvciBhY2lkaWMgYSBzb2x1dGlvbiBpcy4NCiAgDQpUaGUgc2NhdHRlcnBsb3Qgb2YgYE1lcmN1cnlgIHZlcnN1cyBgQ2FsY2l1bWAgYW5kIGBNZXJjdXJ5YCB2ZXJzdXMgYENobG9yb3BoeWxsYCBzZWVtIHRvIGZvbGxvdyB0aGUgdHJlbmQgb2YgYW4gZXhwb25lbnRpYWwgZGVjYXksIGEgY3VydmVkLCBuZWdhdGl2ZSB0cmVuZC4gSG93ZXZlciwgaXQgYWxzbyBzZWVtcyB0aGF0IGBDYWxjaXVtYCBhbmQgYENobG9yb3BoeWxsYCBhcmUgZWFjaCByZXNwZWN0aXZlbHkgaW50ZXJhY3Rpbmcgd2l0aCBgQWxrYWxpbml0eWAsIGFuZCB0aGV5IGJvdGggc2VlbSB0byBpbnRlcmFjdCB3aXRoIGVhY2ggYW5vdGhlci0tIHRoZSBjb3JyZXNwb25kaW5nIHNjYXR0ZXJwbG90cyBhbGwgc2VlbSB0byBoYXZlIGEgcG9zaXRpdmUgYW5kIHJvdWdobHkgbGluZWFyIHRyZW5kLiBJdCBpcyB1bmNsZWFyIGV4YWN0bHkgd2hpY2ggdmFyaWFibGUgaXMgY29uZm91bmRpbmcgdGhlIG90aGVyLCBidXQgdGhlcmUgaXMgYW4gaW50ZXJhY3Rpb24gb2NjdXJyaW5nIGJldHdlZW4gdGhlc2UgdmFyaWFibGVzIHRoYXQgc2hvdWxkIGJlIGludmVzdGlnYXRlZC4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5oZWlnaHQgPSA5fSANCiMgU2hvdyBjb3JyZWxhdGlvbiBtYXRyaXggZm9yIG1lYW5pbmdmdWwgbnVtZXJpYyB2YXJpYWJsZXMNCiMgUmVtb3ZlIDJuZCBjb2x1bW4sIExha2UgbmFtZSkNCmdncGFpcnMoYmFzc1ssLTJdLCBnZ3Bsb3QyOjphZXMoYWxwaGE9MC41KSkgDQpgYGANCiAgDQpJbiB0aGUgcGxvdCBiZWxvdyB3ZSBzZWUgc21hbGxlciBwb2ludHMgdGVuZCB0byBiZSBibHVlciBhbmQgbGVzcyBhbGthbGluZSB3aGlsZSBsYXJnZXIgcG9pbnRzIHRlbmQgdG8gYmUgbW9yZSByZWQgYW5kIG1vcmUgYWxrYWxpbmUuIEFnYWluLCBlYWNoIHBvaW50IHJlcHJlc2VudHMgb25lIGxha2UuIGBDaGxvcm9waHlsbGAgYW5kIGBDYWxjaXVtYCBhcmUgbWFwcGVkIHRvIGNvbG9yIGFuZCBzaXplIHJlc3BlY3RpdmVseSwgc28gdGhlIHBhdHRlcm4gd2Ugc2hvd3MgdGhhdCBgQ2hsb3JvcGh5bGxgLCBgQ2FsY2l1bWAsIGFuZCBgQWxrYWxpbml0eWAgYWxsIHNlZW0gdG8gYmUgZm9sbG93aW5nIHRoZSBzYW1lIG5lZ2F0aXZlIHRyZW5kIGluIHRoZWlyIGludGVyYWN0aW9uIHdpdGggYE1lcmN1cnlgLiANCmBgYHtyfQ0KcCA8LSBiYXNzICU+JSAgICAgICAgICAgICAgICAgIA0KICBnZ3Bsb3QoIGFlcyh4PUFsa2FsaW5pdHkseT1NZXJjdXJ5LCBjb2w9IENobG9yb3BoeWxsLCBzaXplPUNhbGNpdW0pKSArDQogIGdlb21fcG9pbnQoKSArDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdz0iYmx1ZSIsIGhpZ2g9InJlZCIpDQoNCmdncGxvdGx5KHApDQpgYGANCg0KDQpUaGVyZSBpcyBhIHBvdGVudGlhbCBmb3IgY29uZm91bmRpbmcgdmFyaWFibGVzIGluIG91ciBkYXRhLiBUaGVyZSBpcyBzdGlsbCBhIHBvc3NpYmlsaXR5IG9mIHNwYXRpYWwgY29ycmVsYXRpb24gc2luY2UgYWxsIG9mIHRoZXNlIGxha2VzIGV4aXN0IGluIHRoZSB2aWNpbml0eSBvZiBjZW50cmFsIEZsb3JpZGEuIEkgd291bGQgbm90IHNheSB0aGUgaW5kZXBlbmRlbmNlIGFzc3VtcHRpb24gaXMgdXBoZWxkLiANCg0KIyMgVW51c3VhbCBQb2ludHMNCg0KQmVsb3cgaXMgYSBwbG90IG9mIGVhY2ggb2JzZXJ2YXRpb25hbCB1bml0J3Mgc3R1ZGVudGl6ZWQgcmVzaWR1YWwgdmVyc3VzIGl0cyBsZXZlcmFnZS4gUmVjYWxsIHRoYXQgYSBsYWtlIGlzIGFuIG9ic2VydmF0aW9uYWwgdW5pdCwgc28gZWFjaCBwb2ludCByZXByZXNlbnRzIHRoZSByZXNpZHVhbCBhbmQgbGV2ZXJhZ2Ugb2YgYSBwYXJ0aWN1bGFyIGxha2UgaW4gdGhlIGRhdGEgc2V0LiBTaW5jZSBgSURgIGNvcnJlc3BvbmRzIGFsbW9zdCBwZXJmZWN0bHkgdG8gdGhlIHJvdyBudW1iZXJzIGluIHRoZSBkYXRhIHNldCAob25seSB1bnRydWUgZm9yIHJvd3MgNDctNTEpICwgdGhlIG51bWJlciBsYWJlbHMgc2hvd24gb24gdGhlIHBsb3QgaWRlbnRpZnkgdGhlIGxha2VzIGJ5IHRoZWlyIElELiANCg0KYGBge3J9IA0Kb2xzX3Bsb3RfcmVzaWRfbGV2KGJhZC5tb2RlbCRtb2RlbCkNCmBgYA0KICANCklmIHdlIGNvbnNpZGVyIGEgImhpZ2ggbGV2ZXJhZ2UiIGxha2UgdG8gYmUgYSBsYWtlIHdpdGggYSBsZXZlcmFnZSB2YWx1ZSBoaWdoZXIgdGhhbiAzIHRpbWVzIHRoZSBhdmVyYWdlIGxldmVyYWdlIHZhbHVlLCB3ZSBzZWUgdGhhdCB0aGUgb25seSBoaWdoIGxldmVyYWdlIGxha2UgaXMgbGFrZSAxNSwgYSBmYXJtIGxha2UgbmFtZWQgIkZhcm0tMTMiLiBJZiB3ZSB1c2UgYSBtb3JlIGNvbnNlcnZhdGl2ZSB0aHJlc2hvbGQgb2YgfjAuMDc1IHdlIHNlZSBzZXZlcmFsIG1vcmUgbGFrZXMgd2l0aCBoaWdoIGxldmVyYWdlLCBsaXN0ZWQgYmVsb3cuIE5vbmUgb2YgdGhlc2UgaGlnaCBsZXZlcmFnZSBsYWtlcyBhcmUgb3V0bGllcnMsIHNvIHRoZXkgYXJlIG5vdCB1bmR1bHkgaW5mbHVlbmNpbmcgdGhlIG1vZGVsLiBIb3dldmVyLCBsYWtlIDE1LCB0aGUgbGFrZSBuYW1lZCAiRmFybS0xMyIgbWlnaHQgYmUgdW5yZXByZXNlbnRhdGl2ZSBvZiB0aGUgcG9wdWxhdGlvbiBvZiBpbnRlcmVzdC4gSWYgd2UgYXJlIGludGVyZXN0ZWQgaW4gbGVhcm5pbmcgYWJvdXQgIndpbGQiIGxha2VzIHdlIG1heSB3YW50IHRvIHJlbW92ZSBsYWtlIDE1LCB0aGUgIkZhcm0tMTMiIGxha2UgZnJvbSB0aGUgZGF0YS4gSXQgaXMgbm90IGFuIG91dGxpZXIgYnV0IGl0IHNvdW5kcyBsaWtlIGl0IGlzIGEgYm9keSBvZiB3YXRlciBiZWxvbmdpbmcgdG8gYSBmaXNoaW5nIGZhcm0gYW5kIG5vdCBhIG5hdHVyYWxseSBvY2N1cnJpbmcgbGFrZS4gSXQgc2VlbXMgaW5hcHByb3ByaWF0ZSBmb3IgIkZhcm0tMTMiIHRvIGhhdmUgc28gbXVjaCBsZXZlcmFnZSBidXQgbm90IGJlIGluIG91ciBwb3B1bGF0aW9uIG9mIGludGVyZXN0LiBXZSBsZWF2ZSBpdCBpbiBmb3Igbm93IHNpbmNlIGxldmVyYWdlIHdpdGhvdXQgaW5mbHVlbmNlIGlzIGxhcmdlbHkgaW5jb25zZXF1ZW50aWFsIChhbmQgSSBoYXZlIG5vIGV2aWRlbmNlIG90aGVyIHRoYW4gdGhlIG5hbWUgdGhhdCBpdCBpcyBhIGZpc2hpbmcgZmFybSksIGJ1dCB0aGUgYXBwcm9wcmlhdGVuZXNzIG9mIHRoZSBpbmNsdXNpb24gb2YgdGhpcyBwYXJ0aWN1bGFyIGxha2Ugc2hvdWxkIGJlIGNvbnNpZGVyZWQgZnVydGhlci4NCg0KSGVyZSBhcmUgdGhlIGhpZ2ggbGV2ZXJhZ2UgcG9pbnRzIGluIGEgdGFibGU6DQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgdGhlIGN1dG9mZiB2YWx1ZSBvZiBsZXZlcmFnZQ0KbiA8LSBucm93KGJhc3MpDQprIDwtIGxlbmd0aChjb2VmZmljaWVudHMoYmFkLm1vZGVsJG1vZGVsKSkNCnByaW50KHBhc3RlKCJDb25zZXJ2YXRpdmUgTGV2ZXJhZ2UgVGhyZXNob2xkIChVc2VkIGluIHBsb3QpOiIsIDIqIChrL24pKSkNCnByaW50KHBhc3RlKCJMZXZlcmFnZSBUaHJlc2hvbGQ6IiwgMyogKGsvbikpKQ0KYmFzcyRiYWQubW9kZWwubGV2ZXJhZ2VzIDwtIG9sc19sZXZlcmFnZShiYWQubW9kZWwkbW9kZWwpICMgTWFrZSBhIG5ldyBjb2x1bW4gaW4gdGhlIGRhdGEgZm9yIGxldmVyYWdlIHZhbHVlcw0KYmFzcy5sZXYgPC0gYmFzc1tiYXNzJElEICVpbiUgYygxNSwzNSwzLDQxLDE3LDM3KSxdICAgICAgICMgSGlnaCBsZXZlcmFnZSBwb2ludHMgb25seQ0KYmFzcy5sZXYgIyBTaG93IHRhYmxlIG9mIGhpZ2ggbGV2ZXJhZ2UgcG9pbnRzDQpgYGANCg0KSWYgd2UgY29uc2lkZXIgYSBsYWtlIHRvIGJlIGFuICJvdXRsaWVyIiBpZiBpdCBoYXMgYSBzdHVkZW50aXplZCByZXNpZHVhbCBncmVhdGVyIHRoYW4gMywgb25seSBsYWtlIDEsIGxha2UgIkFsbGlnYXRvciIsIGlzIGFuIG91dGxpZXIuIElmIHdlIHVzZSBhIG1vcmUgY29uc2VydmF0aXZlIG91dGxpZXIgdGhyZXNob2xkIHdlIGNhbiBhbHNvIGNvbnNpZGVyIGxha2VzIDIsIDUsIGFuZCA0MCwgdG8gYmUgbW9kZXJhdGUgb3V0bGllcnMuIExha2UgNDAsIExha2UgIlB1enpsZSIsIHdhcyBvbmUgd2UgaGFkIGluaXRpYWxseSBpZGVudGlmaWVkIGFzIGEgbGlrZWx5IG91dGxpZXIgaW4gdGhlIHNjYXR0ZXJwbG90LiBCdXQsIG5vbmUgb2YgdGhlc2Ugb3V0bGllcnMgc2VlbSBoaWdobHkgaW5mbHVlbnRpYWwsIGFzIHNlZW4gYnkgdGhlaXIgbG93IGxldmVyYWdlIHZhbHVlcy4NCg0KSGVyZSBhcmUgdGhlIG91dGxpZXIgcG9pbnRzIGluIGEgdGFibGU6DQoNCmBgYHtyfQ0KYmFzcyRiYWQubW9kZWwucmVzaWR1YWxzIDwtIGJhZC5tb2RlbCRtb2RlbCRyZXNpZHVhbHMgIyBNYWtlIGEgbmV3IGNvbHVtbiBpbiB0aGUgZGF0YSBmb3IgcmF3IHJlc2lkdWFsIHZhbHVlcw0KYmFzcy5vdXQgPC0gYmFzc1tiYXNzJElEICVpbiUgYygxLDIsNSw0MCksXSAgICAgICAgICAgIyBPdXRsaWVycyBvbmx5DQpiYXNzLm91dCAjIFNob3cgdGFibGUgb2Ygb3V0bGllcnMNCmBgYA0KV2hlbiB3ZSBkaXJlY3RseSBleGFtaW5lIGluZmx1ZW5jZSB3aXRoIENvb2sncyBEaXN0YW5jZSB3ZSBzZWUgdGhhdCBsYWtlcyAxLDIsNSwgYW5kIDQwLCB0aGUgc2FtZSBsYWtlcyB3ZSBpZGVudGlmaWVkIGFzIG91dGxpZXJzLCBoYXZlIHRoZSBoaWdoZXN0IENvb2sncyBEaXN0YW5jZS4gTGFrZSAxIGFuZCBMYWtlIDQwLCBuYW1lZCBBbGxpZ2F0b3IgTGFrZSBhbmQgUHV6emxlIGxha2UgcmVzcGVjdGl2ZWx5LCBoYXZlIGRyYXN0aWNhbGx5IGhpZ2hlciBDb29rJ3MgZGlzdGFuY2VzIHRoYW4gbW9zdCBvZiB0aGUgb3RoZXIgZGF0YS4gVGhlc2UgdHdvIHBvaW50cyBoYXZlIHRoZSBoaWdoZXN0IHBvdGVudGlhbCB0byBiZSBpbmZsdWVudGlhbCwgYnV0IG5laXRoZXIgcG9pbnQgaGFzIG11Y2ggbGV2ZXJhZ2UuICANCg0KYGBge3J9IA0Kb2xzX3Bsb3RfY29va3NkX2JhcihiYWQubW9kZWwkbW9kZWwpDQpgYGANCg0KSGVyZSBhcmUgdGhlIG91dGxpZXJzIChncmVlbikgYW5kIGhpZ2ggbGV2ZXJhZ2UgKHJlZCkgcG9pbnRzIG9uIG91ciBpbml0aWFsIHNjYXR0ZXJwbG90IChyZWd1bGFyIHBvaW50cyBpbiBibHVlKS4NCmBgYHtyfSANCmNvbG9yLmNvZGVkIDwtDQpnZ3Bsb3QoYmFzcywgYWVzKHg9QWxrYWxpbml0eSx5PU1lcmN1cnkpKSArDQogIGdlb21fcG9pbnQoZGF0YT1iYXNzLCBzaXplPTMsY29sb3I9ImJsYWNrIixmaWxsPSJibHVlIixzaGFwZT0yMSkrDQogIGdlb21fcG9pbnQoZGF0YT1iYXNzLm91dCxzaXplPTMsY29sb3I9ImJsYWNrIixmaWxsPSJncmVlbiIsc2hhcGU9MjEpKyAjIE91dGxpZXJzDQogIGdlb21fcG9pbnQoZGF0YT1iYXNzLmxldixzaXplPTMsY29sb3I9ImJsYWNrIixmaWxsPSJyZWQiLHNoYXBlPTIxKSsgICAjIEhpZ2ggbGV2ZXJhZ2UgcG9pbnRzDQogIHhsYWIoIkxha2UgQWxrYWxpbml0eSAobWcvTCkiKSArDQogIHlsYWIoIlR5cGljYWwgQW1vdW50IG9mIE1lcmN1cnkgaW4gQmFzcyAobWNnKSIpICsNCiAgZ2d0aXRsZSgiTWVyY3VyeSBDb250ZW50IHZlcnN1cyBMYWtlIEFsa2FsaW5pdHkiKQ0KDQpnZ3Bsb3RseShjb2xvci5jb2RlZCkNCmBgYA0KDQoNCg0KIyBRNiAtIEhpZ2hseSBVbnVzdWFsIFBvaW50cw0KICANCiAgQSkgIEFsbGlnYXRvciBMYWtlIGhhcyB0aGUgaGlnaGVzdCByZXNpZHVhbCBtZXJjdXJ5IHZhbHVlDQogIA0KYGBge3J9DQojIFNob3cgaGlnaGVzdCByYXcgcmVzaWR1YWwgdmFsdWVzIGluIHRoZSBkYXRhDQpoZWFkKGFycmFuZ2UoYmFzcywgZGVzYyhiYWQubW9kZWwucmVzaWR1YWxzKSkpDQpgYGANCg0KICBCKSBMYWtlICJGYXJtLTEzIiBoYXMgdGhlIGhpZ2hlc3QgbGV2ZXJhZ2UNCiAgDQpgYGB7cn0NCiMgU2hvdyBoaWdoZXN0IGxldmVyYWdlIHZhbHVlcyBpbiB0aGUgZGF0YQ0KaGVhZChhcnJhbmdlKGJhc3MsIGRlc2MoYmFkLm1vZGVsLmxldmVyYWdlcykpKQ0KYGBgDQoNCiAgQykgUHV6emxlIExha2UgaGFzIHRoZSBoaWdoZXN0IENvb2sncyBEaXN0YW5jZQ0KDQpgYGB7cn0NCiMgTWFrZSBhIG5ldyBjb2x1bW4gaW4gdGhlIGRhdGEgZm9yIGNvb2tzIGRpc3RhbmNlDQpiYXNzJGJhZC5tb2RlbC5jb29rc0QgPC0gY29va3MuZGlzdGFuY2UoYmFkLm1vZGVsJG1vZGVsKQ0KDQojIFNob3cgaGlnaGVzdCBDb29rJ3MgRGlzdGFuY2UgdmFsdWVzIGluIHRoZSBkYXRhDQpoZWFkKGFycmFuZ2UoYmFzcywgZGVzYyhiYWQubW9kZWwuY29va3NEKSkpDQpgYGANCiAgDQojIFE3IC0gSW5mbHVlbnRpYWwgUG9pbnRzIERpc2N1c3Npb24NCiAgDQpJIGRpc2FncmVlLiBXaGlsZSBMYWtlIDEgYW5kIExha2UgNDAsIEFsbGlnYXRvciBMYWtlIGFuZCBQdXp6bGUgTGFrZSByZXNwZWN0aXZlbHksIGhhdmUgdGhlIGxhcmdlc3QgQ29vaydzIGRpc3RhbmNlcyB2YWx1ZXMsIHRoZXNlIHNhbWUgcG9pbnRzIGhhdmUgc21hbGwgbGV2ZXJhZ2UgdmFsdWVzLCB3aGljaCBzdWdnZXN0cyB0aGV5IGhhdmUgbGl0dGxlIGluZmx1ZW5jZSBvbiB0aGVpciBwcmVkaWN0ZWQgdmFsdWVzLiBUaGV5IGFsc28gaGF2ZSBzbWFsbCBDb29rJ3MgRGlzdGFuY2VzIGluIGdlbmVyYWwgLCBjb25zaWRlcmluZyB0aGUgdGhyZXNob2xkIGZvciBmbGFnZ2luZyBpcyB1c3VhbGx5IGF0IGFyb3VuZCAwLjUuIFRoZXNlIHR3byBwb2ludHMgaGF2ZSB0aGUgcG90ZW50aWFsIHRvIGJlIGluZmx1ZW50aWFsLCBidXQgaGF2ZSBsaXR0bGUgbGV2ZXJhZ2UuIE5vdGljZSBob3cgaW4gdGhlIHNjYXR0ZXJwbG90IGJlbG93IHRoZSBsYWtlcyB3aXRoIGhpZ2ggQ29va3MgRGlzdGFuY2UgaGF2ZSBsb3cgbGV2ZXJhZ2UuIA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfSANCiMgUGxvdCBvZiBDb29rJ3MgRGlzdGFuY2UgdmVyc3VzIExldmVyYWdlDQojIE5vdGljZSBob3cgcG9pbnRzIHdpdGggaGlnaCBDb29rcyBEaXN0YW5jZSBoYXZlIGxvdyBsZXZlcmFnZQ0KY29va3Mudi5sZXYgPC0gDQogIGdncGxvdChkYXRhPWJhc3MsIGFlcyh4PWJhZC5tb2RlbC5jb29rc0QseT1iYWQubW9kZWwubGV2ZXJhZ2VzKSkgKyANCiAgZ2VvbV9wb2ludChzaXplPTMsY29sb3I9ImJsYWNrIixmaWxsPSJibHVlIixzaGFwZT0yMSwgYWxwaGE9LjYpKyANCiAgeGxhYigiQ29va3MgRGlzdGFuY2UiKSArDQogIHlsYWIoIkxldmVyYWdlIikgDQoNCiMgU2hvdyBpbnRlcmFjdGl2ZSBwbG90DQpnZ3Bsb3RseShjb29rcy52LmxldikNCg0KIyBMZXZlcmFnZSBhbmQgQ29vaydzIGRpc3RhbmNlIG9mIHRoZSBvdXRpbGVyIHBvaW50cw0KYmFzc1tiYXNzJElEICVpbiUgYmFzcy5vdXQkSUQsXVssLTI6LTEyXQ0KYGBgDQoNCklmIHlvdSByZW1vdmUgdGhlc2UgbGFrZXMgYW5kIHJlZml0IHRoZSBtb2RlbCB0aGUgZXN0aW1hdGVkIHNsb3BlIGFuZCBpbnRlcmNlcHQgZG8gbm90IGNoYW5nZSB0aGF0IG11Y2gsIG1lYW5pbmcgdGhlIHJlbW92ZWQgcG9pbnRzIGRvbid0IGhhdmUgbXVjaCBvZiBhbiBpbXBhY3Qgb24gdGhlIHBhcmFtZXRlciBlc3RpbWF0aW9uLiBUaGVzZSBwb2ludHMgYXBwZWFyIHRvIGp1c3QgYmUgb3V0bGllcnMgd2l0aCBhIGhpZ2ggQ29vaydzIERpc3RhbmNlLiAgDQoNCkFsbCBvZiB0aGUgZm9sbG93aW5nIG1vZGVscyB1c2UgdGhlIGZvcm11bGEgJFx3aWRlaGF0e01lcmN1cnkgQ29udGVudH09IFx0ZXh0e2ludGVyY2VwdH0rKFx0ZXh0e3Nsb3BlfSkgXHRpbWVzIEFsa2FsaW5pdHkkDQoNCnwgTW9kZWwgTmFtZSB8IE9taXR0ZWQgTGFrZSBJRCB8IE9taXR0ZWQgTGFrZSBOYW1lKHMpIHwgSW50ZXJjZXB0ICAgfCBTbG9wZSB8ICAgICAgIA0KfC0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tfA0KfCBiYWQubW9kZWwgIHwgTm9uZSAgICAgICB8IE5vbmUgICAgICAgICAgICAgIHwgMC43MjIxNjY1NDAgfCAtMC4wMDU1Njc3NTggfA0KfCBsaW5tb2QuMSAgIHwgMSAgICAgICAgICB8IEFsbGlnYXRvciAgICAgICAgIHwgMC42OTIxOTQ2MjIgfCAtMC4wMDUyMDU3ODkgfA0KfCBsaW5tb2QuNDAgIHwgNDAgICAgICAgICB8IFB1enpsZSAgICAgICAgICAgIHwgMC43MjYyNDA0ODUgfCAtMC4wMDYwMjM5MzEgfA0KfCBsaW5tb2QuMS40MHwgMSBhbmQgNDAgICB8IEFsbGlnYXRvciBhbmQgUHV6emxlfCAwLjY5NjMwNjk1IHwgLTAuMDA1NjYxMzUgfA0KDQoNCmBgYHtyfQ0KIyBBYm92ZSB0YWJsZSBpcyBzaG93aW5nIHZhbHVlcyBmcm9tIHRoZXNlIG1vZGVscw0KDQojIENyZWF0ZSAzIG5ldyBtb2RlbHMNCmxpbmRtb2QuMSA8LSBsbShNZXJjdXJ5fkFsa2FsaW5pdHksIGJhc3NbLTEsXSkgICAgIyBNb2RlbCB3aXRob3V0IGxha2UgMQ0KbGluZG1vZC40MCA8LSBsbShNZXJjdXJ5fkFsa2FsaW5pdHksIGJhc3NbLTQwLF0pICAgIyBNb2RlbCB3aXRob3V0IGxha2UgNDANCmxpbmRtb2QuMS40MCA8LSBsbShNZXJjdXJ5fkFsa2FsaW5pdHksIGJhc3NbLWMoMSw0MCksXSkgIyBNb2RlbCB3aXRob3V0IGxha2UgMSBvciBsYWtlIDQwDQpgYGANCiAgDQojIFE4IC0gVHJhbnNmb3JtYXRpb24gRGlzY3Vzc2lvbiAgDQoNCkZpcnN0LCBJIHdvdWxkIHRyeSB0cmFuc2Zvcm1pbmcgYEFsa2FsaW5pdHlgIGFuZCBtb2RlbCBgTWVyY3VyeX5sb2coQWxrYWxpbml0eSlgLiBTaW5jZSBub24tbGluZWFyaXR5IGlzIHRoZSBiaWdnZXN0IGlzc3VlLCB0aGUgZmlyc3Qgc3RlcCBzaG91bGQgYmUgdG8gdHJhbnNmb3JtIHRoZSBwcmVkaWN0b3IuIEkgd291bGQgdHJ5IGEgbG9nIHRyYW5zZm9ybWF0aW9uIGJlY2F1c2UgaXQgc2VlbXMgbGlrZSBtYWtpbmcgdGhlIGxha2VzIHdpdGggbG93ZXIgYEFsa2FsaW5pdHlgIGZ1cnRoZXIgYXBhcnQgYW5kIG1ha2luZyB0aGUgbGFrZXMgd2l0aCBoaWdoZXIgYEFsa2FsaW5pdHlgIGNsb3NlciB3b3VsZCBtYWtlIHRoZSBvdmVyYWxsIHBhdHRlcm4gbG9vayBtb3JlIGxpbmVhci4gSXQncyBoYXJkIHRvIHRydWx5IGlkZW50aWZ5IGFueSBvdGhlciBwcm9ibGVtcyB3aXRoIE5vcm1hbGl0eSBhbmQgSG9tb3NjZWRhc3RpY2l0eSB3aGVuIHRoZSBtb2RlbCBpcyB2ZXJ5IHdyb25nLCBzbyB3ZSB3b3VsZCB0YWtlIGNhcmUgb2YgbGluZWFyaXR5IGZpc3J0LiBJZiB0aGVyZSB3ZXJlIGlzc3VlcyBhZnRlciB0cmFuc2Zvcm1pbmcgYEFsa2FsaW5pdHlgLCB0aGVuIHdlIHdvdWxkIGNvbnNpZGVyIHRyYW5zZm9ybWluZyBgTWVyY3VyeWAgYXMgd2VsbC4NCiAgDQoNCiMgUTkgLSBUcmFuc2Zvcm1pbmcgdGhlIFByZWRpY3RvciAgDQogIA0KSGVyZSB3ZSBjcmVhdGUgdGhlIHR3byBtb2RlbHMgYW5kIHNvbWUgcGxvdHMgdG8gYXNzZXMgdGhlaXIgZml0Lg0KDQpgYGB7cn0NCiMjIyBDcmVhdGluZyBNb2RlbHMgYW5kIFBsb3RzDQoNCiMjIEZpcnN0IG1vZGVsOiBNZXJjdXJ5fmxuKEFsa2FsaW5pdHkpDQoNCmJhc3MkbG9nQWxrYWxpbml0eSA8LSBsb2coYmFzcyRBbGthbGluaXR5KQ0KbG0ubG9nQWxrIDwtIG9sc19yZWdyZXNzKE1lcmN1cnl+bG9nQWxrYWxpbml0eSxkYXRhPWJhc3MpDQoNCiMgQWRkIHJlc2lkdWFscyBhbmQgZml0cyB0byBkYXRhDQpiYXNzJGxvZ0Fsay5yZXNpZCA8LSBsbS5sb2dBbGskbW9kZWwkcmVzaWR1YWxzDQpiYXNzJGxvZ0Fsay5maXRzIDwtIGxtLmxvZ0FsayRtb2RlbCRmaXR0ZWQudmFsdWVzDQoNCiMgTW9kZWwgUGxvdA0KcC5sb2dBbGsgPC0NCiAgZ2dwbG90KGRhdGE9YmFzcywgYWVzKHg9bG9nQWxrYWxpbml0eSwgeT1NZXJjdXJ5KSkgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsZm9ybXVsYT15fngpICsNCiAgZ2VvbV9wb2ludChzaXplPTMsY29sb3I9ImJsYWNrIixmaWxsPSJibHVlIixzaGFwZT0yMSxhbHBoYT0wLjYpICsNCiAgZ2d0aXRsZSgiTWVyY3VyeSB+IGxuKEFsa2FsaW5pdHkpIikgKw0KICB4bGFiKCJsbihMYWtlIEFsa2FsaW5pdHkpIChtZy9MKSIpKw0KICB5bGFiKCJUeXBpY2FsIEFtb3VudCBvZiBNZXJjdXJ5IGluIEJhc3MgKG1jZykiKQ0KICANCiMgUmVzaWR1YWwgdnMgRml0cyBQbG90DQpwLmxvZ0Fsay5yZXNpZC5maXRzIDwtDQogIGdncGxvdChkYXRhPWJhc3MsIGFlcyh4PWxvZ0Fsay5maXRzLCB5PWxvZ0Fsay5yZXNpZCkpICsgDQogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLCBsdHk9Miwgc2l6ZT0xLjIpICsNCiAgZ2VvbV9wb2ludChzaXplPTMsY29sb3I9ImJsYWNrIixmaWxsPSJibHVlIixzaGFwZT0yMSxhbHBoYT0wLjYpICsNCiAgeWxhYigiUmVzaWR1YWxzIikgKw0KICB4bGFiKCJGaXRzIikNCg0KIyBSZXNpZHVhbCBIaXN0b2dyYW0gYW5kIERlbnNpdHkgcGxvdA0KcC5sb2dBbGsuaGlzdCA8LQ0KICBnZ3Bsb3QoYmFzcywgYWVzKHg9bG9nQWxrLnJlc2lkKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoeT0uLmRlbnNpdHkuLiksIGNvbG9yPSJncmV5IixmaWxsPSJ3aGl0ZSIsIGJpbnM9MjApKw0KICBnZW9tX2RlbnNpdHkoYWxwaGE9MC4xLCBmaWxsPSJibHVlIixjb2xvcj0iYmx1ZSIsbHdkPTEpICsNCiAgeGxhYigiUmVzaWR1YWxzIikNCg0KIyMgU2Vjb25kIG1vZGVsOiBNZXJjdXJ5fiBzcXJ0KEFsa2FsaW5pdHkpDQoNCmJhc3Mkc3FydEFsa2FsaW5pdHkgPC0gc3FydChiYXNzJEFsa2FsaW5pdHkpDQpsbS5zcXJ0QWxrIDwtIG9sc19yZWdyZXNzKE1lcmN1cnl+c3FydEFsa2FsaW5pdHksZGF0YT1iYXNzKQ0KDQojIEFkZCByZXNpZHVhbHMgYW5kIGZpdHMgdG8gZGF0YQ0KYmFzcyRzcXJ0QWxrLnJlc2lkIDwtIGxtLnNxcnRBbGskbW9kZWwkcmVzaWR1YWxzDQpiYXNzJHNxcnRBbGsuZml0cyA8LSBsbS5zcXJ0QWxrJG1vZGVsJGZpdHRlZC52YWx1ZXMNCg0KIyBNb2RlbCBQbG90DQpwLnNxcnRBbGsgPC0NCiAgZ2dwbG90KGRhdGE9YmFzcywgYWVzKHg9c3FydEFsa2FsaW5pdHksIHk9TWVyY3VyeSkpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLGZvcm11bGE9eX54LCBjb2xvcj0icmVkIikgKw0KICBnZW9tX3BvaW50KHNpemU9Myxjb2xvcj0iYmxhY2siLGZpbGw9InJlZCIsc2hhcGU9MjEsYWxwaGE9MC42KSArDQogIGdndGl0bGUoIk1lcmN1cnkgfiBzcXJ0KEFsa2FsaW5pdHkpIikrDQogIHhsYWIoInNycXQoTGFrZSBBbGthbGluaXR5KSAobWcvTCkiKSsNCiAgeWxhYigiVHlwaWNhbCBBbW91bnQgb2YgTWVyY3VyeSBpbiBCYXNzIChtY2cpIikNCiAgDQojIFJlc2lkdWFscyB2cyBmaXRzDQpwLnNxcnRBbGsucmVzaWQuZml0cyA8LQ0KICBnZ3Bsb3QoZGF0YT1iYXNzLCBhZXMoeD1zcXJ0QWxrLmZpdHMsIHk9c3FydEFsay5yZXNpZCkpICsgDQogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLCBsdHk9Miwgc2l6ZT0xLjIpICsNCiAgZ2VvbV9wb2ludChzaXplPTMsY29sb3I9ImJsYWNrIixmaWxsPSJyZWQiLHNoYXBlPTIxLGFscGhhPTAuNikgKw0KICB5bGFiKCJSZXNpZHVhbHMiKSArDQogIHhsYWIoIkZpdHMiKQ0KDQojIFJlc2lkdWFsIEhpc3RvZ3JhbSBhbmQgRGVuc2l0eSBwbG90DQpwLnNxcnRBbGsuaGlzdCA8LQ0KICBnZ3Bsb3QoYmFzcywgYWVzKHg9c3FydEFsay5yZXNpZCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4pLCBjb2xvcj0iZ3JleSIsZmlsbD0id2hpdGUiLCBiaW5zPTIwKSsNCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuMSwgZmlsbD0icmVkIixjb2xvcj0icmVkIixsd2Q9MSkgKw0KICB4bGFiKCJSZXNpZHVhbCBEaXN0cmlidXRpb24iKQ0KYGBgDQoNCg0KIEZyb20gVGhlIG1vZGVsIHN1bW1hcmllcywgdGhlIHBsb3RzLCBhbmQgdGhlIGNvbXBhcmlzb24gQ3JpdGVyYSBzdWNoIGFzIHRoZSBBSUMgc2NvcmUgYW5kIHRoZSBBZGp1c3RlZCBSLXNxdWFyZWQgdmFsdWVzLCB0aGUgbW9kZWwgcHJlZGljdGluZyBmaXNoIG1lcmN1cnkgY29udGVudCBmcm9tICRsbihcdGV4dHtMYWtlIEFsa2FsaW5pdHl9KSQgcGVyZm9ybXMgYmV0dGVyIHRoYW4gdGhlIG1vZGVsIHByZWRpY3RpbmcgZmlzaCBtZXJjdXJ5IGNvbnRlbnQgZnJvbSAkXHNxcnR7XHRleHR7QWxrYWxpbml0eX19JCwgYW5kIGJvdGggb2YgdGhlc2UgbW9kZWxzIGFyZSBiZXR0ZXIgdGhhbiB0aGUgb3JpZ2luYWwgbW9kZWwuIFRoZSBtb2RlbHMgcmVzdWx0IGluIHRoZSBmb2xsb3dpbmcgZXF1YXRpb25zOiAgDQogICANCiAgIFxbXHdpZGVoYXR7TWVyY3VyeSBDb250ZW50fT0xLjExNzMrKC0wLjIwMTMpIFx0aW1lcyBcbG4oQWxrYWxpbml0eSlcXQ0KICAgDQogICBcW1x3aWRlaGF0e01lcmN1cnkgQ29udGVudH09MC45MTcyKygtMC4wNzU4KSBcdGltZXMgXHNxcnR7QWxrYWxpbml0eX1cXSAgDQogIA0KQmVsb3cgYXJlIHRoZSBzdW1tYXJpZXMgZm9yIGVhY2ggbW9kZWwuDQogDQpgYGB7cn0NCiMgRGlzcGxheSBib3RoIG1vZGVsIHN1bW1hcmllcw0KDQpsbS5sb2dBbGsgICMgTWVyY3VyeX5sbihBbGthbGluaXR5KSBtb2RlbCBzdW1tYXJ5DQpsbS5zcXJ0QWxrICMgTWVyY3VyeX4gc3FydChBbGthbGluaXR5KSBtb2RlbCBzdW1tYXJ5DQpgYGANCiAgDQpIZXJlIHdlIHBsb3QgdGhlIHJlZ3Jlc3Npb24gbGluZSBmb3IgYm90aCBtb2RlbHMsIGFsb25nIHdpdGggdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGVpciByZXNwZWN0aXZlIHJlc2lkdWFscy4gQ29tcGFyaW5nIHRoZSBzY2F0dGVycGxvdHMsIHRoZSBsb2cgdHJhbnNmb3JtYXRpb25zIChibHVlKSBzZWVtcyB0byBoYXZlIGRvbmUgdGhlIGJlc3Qgam9iIGluIG1ha2luZyB0aGUgZGF0YSBsaW5lYXIuIFRoZSBwbG90IHdoaWNoIHVzZXMgdGhlIHNxdWFyZSByb290IHRyYW5zZm9ybWF0aW9uIChyZWQpIHN0aWxsIHNlZW1zIHRvIGhhdmUgYSBjdXJ2ZWQgcGF0dGVybiwgdGhlIHBvaW50cyBhcmUgbW9zdGx5IGFib3ZlIHRoZSByZWdyZXNzaW9uIGxpbmUgZm9yIGhpZ2hlc3QgYW5kIGxvd2VzdCB2YWx1ZXMgb2YgdGhlIHByZWRpY3Rvci4gVGhpcyBjdXJ2ZWQgcGF0dGVybiBiZWNvbWVzIG1vcmUgYXBwYXJlbnQgaW4gdGhlIFJlc2lkdWFscyB2cyBGaXRzIHBsb3QgZm9yIHRoaXMgbW9kZWwgKHJlZCkuIFRoZSByZWQgcmVzaWR1YWxzIGNsZWFybHkgaGF2ZSBhIGN1cnZlZCwgYW5kIHBvdGVudGlhbGx5IGhldGVyb3NjZWRhc3RpYywgcGF0dGVybiwgYXMgd2VsbCBhcyBhIGNsZWFyIG91dGxpZXIuIFRoZSBibHVlIHJlc2lkdWFscyBhbHNvIGhhdmUgdHdvIHBvdGVudGlhbCBvdXRsaWVycywgYnV0IHRoZSBvdmVyYWxsIHBhdHRlcm4gaW4gdGhlIHBsb3Qgc2VlbXMgcmFuZG9tIGFuZCBob21vc2NlZGFzdGljLg0KICANCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEwfQ0KI1Bsb3QgcmVzdWx0cyBzaWRlIGJ5IHNpZGUNCmdyaWQuYXJyYW5nZShwLmxvZ0FsaywgICAgICAgICAgICBwLnNxcnRBbGssDQogICAgICAgICAgICAgcC5sb2dBbGsucmVzaWQuZml0cywgcC5zcXJ0QWxrLnJlc2lkLmZpdHMsDQogICAgICAgICAgICAgcC5sb2dBbGsuaGlzdCwgICAgICAgcC5zcXJ0QWxrLmhpc3QsDQogICAgICAgICAgICAgbmNvbD0yKQ0KYGBgDQogIA0KDQpIZXJlIHdlIHVzZSBkaWZmZXJlbnQgaW5mb3JtYXRpb24gY3JpdGVyaW9uIHRvIGNvbXBhcmUgdGhlIHRocmVlIG1vZGVscyB3ZSd2ZSBtYWRlIHNvIGZhci4gV2Ugc2VlIHRoYXQgdGhlIG9yaWdpbmFsIG1vZGVsIHByZWRpY3RpbmcgYmFzcyBNZXJjdXJ5IGNvbnRlbnQgZnJvbSByYXcgbGFrZSBBbGthbGluaXR5LCB3aGljaCBpcyBuYW1lZCBgYmFkLm1vZGVsYCwgcGVyZm9ybXMgdGhlIHdvcnN0LiBgYmFkLm1vZGVsYCBoYXMgdGhlIGhpZ2hlc3QgQUlDLCBBSUNjLCBhbmQgQklDIHNjb3JlcyBhbmQgdGhlIGxvd2VzdCBSLXNxdWFyZWQgYW5kIEFkanVzdGVkIFItc3F1YXJlZCB2YWx1ZXMsIG1lYW5pbmcgaXQgaGFzIHRoZSBwb29yZXN0IG1vZGVsIGFtb25nIHRoZSB0aHJlZSBhbmQgaXQgZXhwbGFpbnMgdGhlIGxlYXN0IGFtb3VudCBvZiB2YXJpYXRpb24gaW4gdGhlIGRhdGEuIExvb2tpbmcgYXQgdGhlIGJvdHRvbSB0d28gcm93cyBvZiB0aGUgY29tcGFyaXNvbiBvdXRwdXQgYmVsb3csIHdlIHNlZSB0aGF0IHRoZSBtb2RlbCBwcmVkaWN0aW5nIGJhc3MgTWVyY3VyeSBjb250ZW50IGZyb20gJFxsbiggXHRleHR7bGFrZSBBbGthbGluaXR5fSkkIChzZWNvbmQgcm93KSBpcyB0aGUgYmVzdCBtb2RlbC4gSXQgaGFzIHRoZSBsb3dlc3QgQUlDLCBBSUNjLCBhbmQgQklDIHNjb3JlcyBhbmQgZXhwbGFpbnMgdGhlIG1vc3QgdmFyaWF0aW9uIGluIHRoZSBkYXRhIChoaWdoZXN0IFItc3F1YXJlZCB2YWx1ZSkuIFRoZSBtb2RlbCBwcmVkaWN0aW5nIGJhc3MgTWVyY3VyeSBjb250ZW50IGZyb20gJFxzcXJ0e1x0ZXh0e2xha2UgQWxrYWxpbml0eX19JCAodGhpcmQgcm93KSBhbG1vc3QgZXhwbGFpbnMgYXMgbXVjaCB2YXJpYXRpb24gaW4gdGhlIGRhdGEgYXMgdGhlIHByZXZpb3VzIG1vZGVsLCBidXQgaGFzIHdvcnNlIEFJQywgQUlDYywgYW5kIEJJQyBzY29yZXMuDQoNCmBgYHtyfQ0KIyBDb21wYXJlIGJvdGggbW9kZWxzIHdpdGggdGhlIG9yaWdpbmFsDQpjb21wYXJlTE0oYmFkLm1vZGVsJG1vZGVsLCBsbS5sb2dBbGskbW9kZWwsIGxtLnNxcnRBbGskbW9kZWwpDQpgYGANCg0KDQojIFExMCAtIEV4YW1pbmluZyB0aGUgTG9nIFRyYW5zZm9ybWF0aW9uDQogIA0KIyMgTW9kZWwgU3VtbWFyeQ0KICANCmBgYHtyfQ0KIyBEaXNwbGF5IE1vZGVsIFN1bW1hcnkNCmxtLmxvZ0Fsaw0KDQpwLmxvZ0Fsaw0KYGBgDQogIA0KVGhlIHNjYXR0ZXJwbG90IG9mIG91ciB0cmFuc2Zvcm1lZCBkYXRhIHNob3dzIGEgbmVnYXRpdmUgbGluZWFyIGFzc29jaWF0aW9uIGJldHdlZW4gYmFzcyBtZXJjdXJ5IGNvbnRlbnQgYW5kIGxvZyBsYWtlIEFsa2FsaW5pdHkuIEJ5IHRyYW5zZm9ybWluZyB0aGUgZGF0YSwgd2UgaGF2ZSBlbGltaW5hdGVkIHRoZSBpc3N1ZSBvZiBub24tbGluZWFyaXR5LiBOb3csIGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIGlzIGFuIGFwcHJvcHJpYXRlIHdheSB0byBtb2RlbCBvdXQgZGF0YS4NCiAgDQpUaGUgbW9kZWwgcHJlZGljdGluZyBiYXNzIE1lcmN1cnkgY29udGVudCBmcm9tIGxvZyBsYWtlIEFsa2FsaW5pdHkgaXM6ICANCiAgDQogXFtcd2lkZWhhdHtNZXJjdXJ5IENvbnRlbnR9PTEuMTE3MysoLTAuMjAxMykgXHRpbWVzIFxsbihBbGthbGluaXR5KVxdDQogIA0KV2hlcmUgJE1lcmN1cnkgQ29udGVudCQgaXMgdGhlIGFtb3VudCBvZiBtZXJjdXJ5IGluIG1jZyBmb3VuZCBpbiBhIHR5cGljYWwgbGFyZ2Vtb3V0aCBiYXNzIGZyb20gYSBsYWtlIGFuZCAkXGxuKEFsa2FsaW5pdHkpJCBpcyB0aGUgbmF0dXJhbCBsb2cgb2YgdGhlIG1lYXN1cmUgb2YgQWxrYWxpbml0eSBvZiBhIGxha2UgaW4gbWcvTC4gV2l0aCBhIHAtdmFsdWUgb2YgYWxtb3N0IDAgZm9yIHRoZSBBTk9WQSBGLXRlc3QgZm9yIGxpbmVhciBhc3NvY2lhdGlvbiwgd2UgaGF2ZSBzdHJvbmcgZXZpZGVuY2UgdG8gc3VnZ2VzdCB0aGF0IHRoZXJlIGlzIHRydWx5IGEgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGxvZyBsYWtlIEFsa2FsaW5pdHkgYW5kIGJhc3MgbWVyY3VyeSBjb250ZW50LiBGb3IgZXZlcnkgYWRkaXRpb25hbCBsb2cgbWcvTCBBbGthbGluaXR5IG9mIGEgbGFrZSwgd2UgZXhwZWN0IHRoZSBtZXJjdXJ5IGNvbnRlbnQgb2YgYSB0eXBpY2FsIGZpc2ggZnJvbSB0aGF0IGxha2UgdG8gZGVjcmVhc2UgYnkgMC4yMDEzIG1jZyBvZiBtZXJjdXJ5LiBUaGlzIG1vZGVsIGNhbiBhY2NvdW50IGZvciB+NTIuNiUgb2YgdGhlIHZhcmlhdGlvbiBpbiB0eXBpY2FsIGJhc3MgbWVyY3VyeSBjb250ZW50IHdpdGggdmFyaWF0aW9uIGluIGxvZyBsYWtlIEFsa2FsaW5pdHkuIE9uIGF2ZXJhZ2UsIG91ciBtb2RlbCBpcyBvZmYgYnkgYWJvdXQgMC4wNTUgbWNnIHdoZW4gcHJlZGljdGluZyBiYXNzIG1lcmN1cnkgY29udGVudCBmcm9tIGxvZyBsYWtlIGFsa2FsaW5pdHkuIFJlY2FsbCB0aGF0IHRoZSBSLXNxdWFyZWQgYW5kIE1TRSBmb3Igb3VyIGluaXRpYWwgbW9kZWwgd2l0aCBubyB0cmFuc2Zvcm1hdGlvbnMgd2VyZSAzOS40OSUgYW5kIDAuNzEgcmVzcGVjdGl2ZWx5LCBhIHdvcnNlIHBlcmZvcm1hbmNlIHRoYW4gdGhpcyBtb2RlbC4gQWxzbyByZWNhbGwgdGhhdCB1c2luZyB0aGlzIHRyYW5zZm9ybWF0aW9uIGFzIG9wcG9zZWQgdG8gdGhlIHJhdyBkYXRhIHZhbHVlcyB5aWVsZHMgYSBiZXR0ZXIgbW9kZWwgYmFzZWQgb24gdGhlIEFJQyBhbmQgQklDIGluZm9ybWF0aW9uIGNyaXRlcmlhIGFzIHdlbGwuIFRoaXMgbW9kZWwgaXMgYSBiZXR0ZXIgZml0IHRoYW4gdGhlIG9yaWdpbmFsLiAgDQogIA0KIyMgU0xSIEFzc3VtcHRpb25zIChMLkkuTi5FKQ0KDQpgYGB7cn0gIA0KcC5sb2dBbGsucXEgPC0gb2xzX3Bsb3RfcmVzaWRfcXEobG0ubG9nQWxrJG1vZGVsLCBwcmludF9wbG90ID0gRkFMU0UpDQpwLmxvZ0Fsay5yZXNpZC5maXRzIDwtIHAubG9nQWxrLnJlc2lkLmZpdHMgKyBnZ3RpdGxlKCJSZXNpZHVhbHMgdnMgRml0cyIpDQpwLmxvZ0Fsay5oaXN0IDwtIHAubG9nQWxrLmhpc3QgKyBnZ3RpdGxlKCJSZXNpZHVhbCBEaXN0cmlidXRpb24iKQ0KZ3JpZC5hcnJhbmdlKHAubG9nQWxrLCBwLmxvZ0Fsay5yZXNpZC5maXRzLHAubG9nQWxrLmhpc3QsIHAubG9nQWxrLnFxICkNCmBgYA0KDQoNClRoZSBzY2F0dGVycGxvdCBvZiB0aGUgdHJhbnNmb3JtZWQgZGF0YSBhcHBlYXJzIHRvIGZvbGxvdyBhIGxpbmVhciB0cmVuZCBhbmQgdGhlIFJlc2lkdWFscyB2cyBGaXRzIHBsb3QgYXBwZWFycyB0byBoYXZlIGEgcmFuZG9tIHBhdHRlcm4sICoqbGluZWFyaXR5IGlzIHNhdGlzZmllZCoqIHdpdGggdGhlIGxvZyB0cmFuc2Zvcm1hdGlvbi4gIA0KICANCkF0IGZpcnN0IGdsYW5jZSB0aGVyZSBhcHBlYXJzIHRvIGJlICBhIHNsaWdodCBmYW5uaW5nIHBhdHRlcm4gaWYgeW91IGlnbm9yZSB0aGUgdHdvIHBvc3NpYmxlIG91dGxpZXJzIGluIHRoZSBSZXNpZHVhbCB2cy4gRml0cyBwbG90LCAsIGJ1dCBhbiBGLXRlc3QgZm9yIGhldGVyb3NjZWRhc3RpY2l0eSBnaXZlcyB1cyBhIHAtdmFsdWUgb2YgfjAuMTQ+MC41PSRcYWxwaGEkLiBXZSBmYWlsIHRvIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIHRoYXQgb3VyIGRhdGEgaXMgaG9tb3NjZWRhc3RpYywgdGhlcmUgaXMgbm90IHN0cm9uZyBlbm91Z2ggZXZpZGVuY2UgdG8gc3VnZ2VzdCBhIGRldmlhdGlvbiBmcm9tIGhvbW9zY2VkYXN0aWNpdHkgaW4gb3VyIHJlc2lkdWFscyAodGVzdCBvdXRwdXQgYmVsb3cpLiBXZSBjYW4gYXNzdW1lIHRoZSAqKmVxdWFsIHZhcmlhbmNlKiogY29uZGl0aW9uIGlzIHNhdGlzZmllZC4NCg0KYGBge3J9DQojIEFOT1ZBIEYtdGVzdCBmb3IgSGV0ZXJvc2tlZGFzdGljaXR5DQpvbHNfdGVzdF9mKGxtLmxvZ0FsayRtb2RlbCkNCmBgYA0KICANCldlIGNhbiBzZWUgZnJvbSB0aGUgZGVuc2l0eSBwbG90IHRoYXQgdGhlIHJlc2lkdWFsIGRpc3RyaWJ1dGlvbiBpcyBwb3NpdGl2ZWx5IHNrZXdlZC4gVGhlIGRpcCBiZWxvdyB0aGUgbGluZSBpbiB0aGUgUVEgcGxvdCBpbmRpY2F0ZXMgdGhpcyBza2V3LCBhbmQgdGhlIGRldmlhdGlvbiBmcm9tIHRoZSBsaW5lIGluIHRoZSB1cHBlciB0YWlscyBvZiB0aGUgZGlzdHJpYnV0aW9uIGluZGljYXRlcyB0aGF0IHRoZXJlIGlzIG1vcmUgZGF0YSBpbiB0aGUgdGFpbHMgb2YgdGhlIHJlc2lkdWFsIGRpc3RyaWJ1dGlvbiB0aGFuIGluIGEgTm9ybWFsIGRpc3RyaWJ1dGlvbi4gU3RhdGlzdGljYWwgdGVzdHMgZm9yIG5vcm1hbGl0eSBjb25maXJtIHRoaXMuIE91dCBvZiBmb3VyIGh5cG90aGVzaXMgdGVzdCwgZWFjaCB3aXRoIG51bGwgaHlwb3RoZXNlcyB0aGF0IG91ciByZXNpZHVhbHMgKmFyZSogbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIHRoZSBtYWpvcml0eSBvZiB0aGUgcmVzdWx0cyBoYXZlIGEgcC12YWx1ZSBvZiBhbG1vc3QgMCB3aGljaCBsZWFkcyB1cyB0byByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyAodGVzdCByZXN1bHRzIGJlbG93KS4gV2UgaGF2ZSBldmlkZW5jZSB0byBzdWdnZXN0IHRoYXQgKipvdXIgcmVzaWR1YWxzIGFyZSBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQqKiwgd2hpY2ggZWNob2VzIHdoYXQgd2Ugc2F3IGluIHRoZSBwbG90cy4gSG93ZXZlciwgdGhlIGh5cG90aGVzaXMgdGVzdCBhbmQgY29uZmlkZW5jZSBpbnRlcnZhbHMgdGhhdCBnaXZlcyB1cyBvdXIgZXN0aW1hdGVzIGZvciAkXGJldGFfMCQgYW5kICRcYmV0YV8xJCwgdGhlIGludGVyY2VwdCBhbmQgc2xvcGUgcmVzcGVjdGl2ZWx5IG9mIHRoZSByZWdyZXNzaW9uIGVxdWF0aW9uLCBhcmUgcm9idXN0IHRvIG5vbi1ub3JtYWwgcmVzaWR1YWxzLiBUaGlzIGRldmlhdGlvbiBmcm9tIG5vcm1hbGl0eSBtYXkgb25seSBiZSBvZiBjb25jZXJuIGlmIHdlIHdpc2ggdG8gY3JlYXRlIHByZWRpY3Rpb24gaW50ZXJ2YWxzLCBidXQgaXQgaXMgbm90IHdvcnRoIHRocm93aW5nIGF3YXkgdGhlIG1vZGVsLg0KDQoNCmBgYHtyfQ0Kb2xzX3Rlc3Rfbm9ybWFsaXR5KGxtLmxvZ0FsayRtb2RlbCkNCmBgYA0KDQpBZ2FpbiwgaXQgd291bGQgYmUgd29ydGggaW52ZXN0aWdhdGluZyBzZXJpYWwgYW5kIHNwYXRpYWwgY29ycmVsYXRpb25zIGJ1dCBpbiB0aGUgYWJzZW5jZSBvZiB0aGlzIGluZm9ybWF0aW9uLCB3ZSBjYW4gZXhhbWluZSB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGV4aXN0aW5nIHZhcmlhYmxlcyBmb3IgY29uZm91bmRpbmcuICANCg0KQmVsb3cgaXMgYSBtYXRyaXggc2hvd2luZyB0aGUgcGFpcndpc2Ugc2NhdHRlcnBsb3QgYW5kIHBhaXJ3aXNlIGNvcnJlbGF0aW9uIGJldHdlZW4gZXZlcnkgbWVhbmluZ2Z1bCBudW1lcmljIHZhcmlhYmxlIGluIHRoZSBkYXRhIHNldC4gVW5pdmFyaWF0ZSBkZW5zaXR5IHBsb3RzIGFyZSBkaXNwbGF5ZWQgYWxvbmcgdGhlIGRpYWdvbmFsLg0KDQpBcyBiZWZvcmUsIGBtYXhgLCBgbWluYCwgYW5kIGBNZXJjdXJ5YCBhcmUgYWxsIGhpZ2hseSBjb3JyZWxhdGVkIHdpdGggZWFjaCBvdGhlci4gDQoNCkxpa2UgYmVmb3JlIHdpdGggcmF3IGBBbGthbGluaXR5YCwgd2Ugc2VlIGEgdHJlbmQgaW4gdGhlIHNjYXR0ZXJwbG90IG9mIGBwSGAgdmVyc3VzIGBsb2dBbGthbGluaXR5YCBhbmQgYSBzaWduaWZpY2FudCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB0d28uIFRoaXMgdGltZSB0aGUgcmVsYXRpb25zaGlwIGlzIGEgc3Ryb25nIHBvc2l0aXZlIGxpbmVhciBvbmUgYXMgb3Bwb3NlZCB0byBhIGN1cnZlZCByZWxhdGlvbnNoaXAgaXQgaGFzIHdpdGggdGhlIHJhdyB2YXJpYWJsZS4gQWdhaW4sIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlc2UgdmFyaWFibGVzIGlzIG5vdCBzdXJwcmlzaW5nIGNvbnNpZGVyaW5nIHBIIG1lYXN1cmVzIGFjaWRpdHkgYW5kIGFsa2FsaW5pdHkuDQogIA0KQXMgYmVmb3JlLCB0aGUgc2NhdHRlcnBsb3Qgb2YgYE1lcmN1cnlgIHZlcnN1cyBgQ2FsY2l1bWAgYW5kIGBNZXJjdXJ5YCB2ZXJzdXMgYENobG9yb3BoeWxsYCBzZWVtIHRvIGZvbGxvdyB0aGUgdHJlbmQgb2YgYW4gZXhwb25lbnRpYWwgZGVjYXksIGEgY3VydmVkLCBuZWdhdGl2ZSB0cmVuZC4gYENhbGNpdW1gIGFuZCBgQ2hsb3JvcGh5bGxgIGVhY2ggcmVzcGVjdGl2ZWx5IGhhdmUgYSBwb3NpdGl2ZSBhc3NvY2lhdGlvbiB3aXRoIGBsb2dBbGthbGluaXR5YCBhbmQgYXJlIGVhY2ggaW50ZXJhY3Rpbmcgd2l0aCBlYWNoIGFub3RoZXIuIFRoaXMgcmVsYXRpb25zaGlwIGxvb2tlZCByb3VnaGx5IGxpbmVhciB3aXRoIHRoZSByYXcgYEFsa2FsaW5pdHlgIGJ1dCB0aGUgcmVsYXRpb25zaGlwIHdpdGggYGxvZ0Fsa2FsaW5pdHlgIGlzIGN1cnZlZC4gSXQgaXMgdW5jbGVhciBleGFjdGx5IHdoaWNoIHZhcmlhYmxlIGlzIGNvbmZvdW5kaW5nIHRoZSBvdGhlciwgYnV0IHRoZXJlIGlzIGFuIGludGVyYWN0aW9uIG9jY3VycmluZyBiZXR3ZWVuIHRoZXNlIHZhcmlhYmxlcyB0aGF0IHNob3VsZCBiZSBpbnZlc3RpZ2F0ZWQuDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuaGVpZ2h0ID0gOX0gDQojIFNob3cgY29ycmVsYXRpb24gbWF0cml4IGZvciBtZWFuaW5nZnVsIG51bWVyaWMgdmFyaWFibGVzDQojIE9ubHkgdXNlIHRoZSBtZWFuaW5nZnVsIG51bWVyaWMgdmFyaWFibGVzIGZyb20gdGhlIG9yaWdpbmFsIGRhdGEgc2V0IGFuZCB0aGUgdHJhbnNmb3JtZWQgbG4oQWxrYWxpbml0eSkgdmFyaWFibGUNCg0Ka2VlcCA8LSBjKCJwSCIsIkNhbGNpdW0iLCJDaGxvcm9waHlsbCIsIk5vX3NhbXBsZXMiLCJtaW4iLCJtYXgiLCJNZXJjdXJ5IiwiYWdlX2RhdGEiLCJsb2dBbGthbGluaXR5IiwiQWxrYWxpbml0eSIpIA0KDQpnZ3BhaXJzKGJhc3NbLGtlZXBdLCBnZ3Bsb3QyOjphZXMoYWxwaGE9MC41KSkgDQpgYGANCiAgIA0KIEluIHRoZSBwbG90IGJlbG93IGVhY2ggcG9pbnQgcmVwcmVzZW50cyBvbmUgbGFrZS4gTGFrZXMgd2l0aCBtb3JlIGNobG9yb3BoeWxsIGhhdmUgbW9yZSByZWQgcG9pbnRzIGFuZCBsYWtlcyB3aXRoIG1vcmUgY2FsY2l1bSBhcmUgcmVwcmVzZW50ZWQgYnkgdGhlIGxhcmdlciBwb2ludHMuIFdlIHNlZSBzbWFsbGVyIHBvaW50cyB0ZW5kIHRvIGJlIGJsdWVyIGFuZCBoYXZlIGxvd2VyIGxvZyBhbGthbGluZSB2YWx1ZXMsIHdoaWxlIGxhcmdlciBwb2ludHMgdGVuZCB0byBiZSBtb3JlIHJlZCBhbmQgaGF2ZSBoaWdoZXIgbG9nIGFsa2FsaW5lIHZhbHVlcy4gU28sIHRoZSBwYXR0ZXJuIHdlIHNob3dzIHRoYXQgYENobG9yb3BoeWxsYCwgYENhbGNpdW1gLCBhbmQgYGxvZ0Fsa2FsaW5pdHlgIGFsbCBzZWVtIHRvIGJlIGZvbGxvd2luZyB0aGUgc2FtZSBuZWdhdGl2ZSB0cmVuZCBpbiB0aGVpciBpbnRlcmFjdGlvbiB3aXRoIGBNZXJjdXJ5YC4gV2UgY291bGQgc2VlIHRoaXMgdHJlbmQgd2hlbiB3ZSBtYXBwZWQgYENobG9yb3BoeWxsYCBhbmQgYENhbGNpdW1gIHRvIGNvbG9yIGFuZCBzaXplIG9uIHRoZSBgTWVyY3VyeX5BbGthbGluaXR5YCBwbG90IGZyb20gYmVmb3JlLCBidXQgdGhlIHRyZW5kIGlzIGNsZWFyZWQgd2hlbiB0YWtlIHRoZSBsb2cgb2YgQWxrYWxpbml0eS4NCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfSANCnAyIDwtIGJhc3MgJT4lICAgICAgICAgICAgICAgICAgDQogIGdncGxvdCggYWVzKHg9bG9nQWxrYWxpbml0eSx5PU1lcmN1cnksIGNvbD0gQ2hsb3JvcGh5bGwsIHNpemU9Q2FsY2l1bSkpICsNCiAgZ2VvbV9wb2ludCgpICsgeGxhYigibG4oTGFrZSBBbGthbGluaXR5KSAoaW4gbWcvTCkiKSArDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdz0iYmx1ZSIsIGhpZ2g9InJlZCIpDQoNCmdncGxvdGx5KHAyKQ0KYGBgDQogIA0KVGhlcmUgaXMgc3RpbGwgYSBwb3RlbnRpYWwgZm9yIGNvbmZvdW5kaW5nIHZhcmlhYmxlcyBpbiBvdXIgZGF0YS4gQWxzbywgdGhlIHBvc3NpYmlsaXR5IG9mIHNwYXRpYWwgY29ycmVsYXRpb24gc2VlbXMgdmVyeSBwbGF1c2libGUgc2luY2UgYWxsIG9mIHRoZXNlIGxha2VzIGV4aXN0IGluIHRoZSB2aWNpbml0eSBvZiBjZW50cmFsIEZsb3JpZGEuIFdpdGhvdXQgYW55IHdheSB0byBhc3Nlc3MgdGhpcywgSSB3b3VsZCBub3QgYmUgY29tZm9ydGFibGUgaW4gYXNzdW1pbmcgaW5kZXBlbmRlbmNlIGlzIHVwaGVsZC4gDQogIA0KIyMgVW51c3VhbCBQb2ludHMgIA0KICANCkJlbG93IGlzIGEgcGxvdCBvZiBlYWNoIG9ic2VydmF0aW9uYWwgdW5pdCdzIHN0dWRlbnRpemVkIHJlc2lkdWFsIHZlcnN1cyBpdHMgbGV2ZXJhZ2UuIFJlY2FsbCB0aGF0IGEgbGFrZSBpcyBhbiBvYnNlcnZhdGlvbmFsIHVuaXQsIHNvIGVhY2ggcG9pbnQgcmVwcmVzZW50cyB0aGUgcmVzaWR1YWwgYW5kIGxldmVyYWdlIG9mIGEgcGFydGljdWxhciBsYWtlIGluIHRoZSBkYXRhIHNldC4gU2luY2UgYElEYCBjb3JyZXNwb25kcyBhbG1vc3QgcGVyZmVjdGx5IHRvIHRoZSByb3cgbnVtYmVycyBpbiB0aGUgZGF0YSBzZXQgKG9ubHkgdW50cnVlIGZvciByb3dzIDQ3LTUxKSAsIHRoZSBudW1iZXIgbGFiZWxzIHNob3duIG9uIHRoZSBwbG90IGlkZW50aWZ5IHRoZSBsYWtlcyBieSB0aGVpciBJRC4gDQoNCmBgYHtyfSANCm9sc19wbG90X3Jlc2lkX2xldihsbS5sb2dBbGskbW9kZWwpDQpgYGANCiAgDQpXZSBzZWUgdGhhdCB0aGUgb25seSBoaWdoIGxldmVyYWdlIGxha2UgaXMgaW4gcm93IDUwLCBsYWtlIDQ5IG9yIFRzYWxhIEFwb3BrYSBMYWtlLiBMYWtlcyAxLDIsIGFuZCA0MCwgQWxsaWdhdG9yLCBBbm5pZSwgYW5kIFB1enpsZSBMYWtlIHJlc3BlY3RpdmVseSwgYXJlIG91dGxpZXJzLiBXZSBoYXZlIG5vIGhpZ2hseSBpbmZsdWVudGlhbCBwb2ludHMsIGJ1dCB0aGVyZSBpcyBvbmUgdW5pZGVudGlmaWVkIHBvaW50IHRoYXQgbWF5IGJlIGNsYXNzaWZpZWQgYXMgc3VjaCBpZiBtb3JlIGNvbnNlcnZhdGl2ZSB0aHJlc2hvbGRzIGFyZSBjaG9zZW4uIA0KICANCk5vdGUgdGhhdCB0aGVyZSBpcyBvbmUgdW5sYWJlbGVkIGJsdWUgcG9pbnQgb24gdGhlIHBsb3QgYWJvdmUgdGhhdCBpcyB2ZXJ5IGNsb3NlIHRvIGNyb3NzaW5nIGJvdGggdGhlIGxldmVyYWdlIGFuZCByZXNpZHVhbCB0aHJlc2hvbGQsIGxha2UgNSBvciBCcmljayBMYWtlLiBUaGlzIGlzIHRoZSBvbmx5IHBvaW50IHRoYXQgaXMgY2xvc2UgdG8gYmVpbmcgYW4gaW5mbHVlbnRpYWwgcG9pbnQuDQoNCkhlcmUgYXJlIHRoZSBoaWdoIGxldmVyYWdlIHBvaW50cyBpbiBhIHRhYmxlLCBpbmNsdWRpbmcgQnJpY2sgTGFrZToNCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgY3V0b2ZmIHZhbHVlIG9mIGxldmVyYWdlDQpuIDwtIG5yb3coYmFzcykNCmsgPC0gbGVuZ3RoKGNvZWZmaWNpZW50cyhsbS5sb2dBbGskbW9kZWwpKQ0KcHJpbnQocGFzdGUoIkNvbnNlcnZhdGl2ZSBMZXZlcmFnZSBUaHJlc2hvbGQgKFVzZWQgaW4gcGxvdCk6IiwgMiogKGsvbikpKQ0KcHJpbnQocGFzdGUoIkxldmVyYWdlIFRocmVzaG9sZDoiLCAzKiAoay9uKSkpDQpiYXNzJGxtLmxvZ0Fsay5sZXZlcmFnZXMgPC0gb2xzX2xldmVyYWdlKGxtLmxvZ0FsayRtb2RlbCkgIyBNYWtlIGEgbmV3IGNvbHVtbiBpbiB0aGUgZGF0YSBmb3IgbGV2ZXJhZ2UgdmFsdWVzDQpiYXNzLmxldiA8LSBiYXNzW2Jhc3MkbG0ubG9nQWxrLmxldmVyYWdlcz4wLjA3LF0gICAgICAgICAgIyBIaWdoIGxldmVyYWdlIHBvaW50cyBvbmx5DQpiYXNzLmxldg0KYGBgDQoNCkhlcmUgYXJlIHRoZSBvdXRsaWVyIHBvaW50cyBpbiBhIHRhYmxlLCBpbmNsdWRpbmcgQnJpY2sgTGFrZToNCg0KYGBge3J9DQpiYXNzJGxtLmxvZ0Fsay5yZXNpZHVhbHMgPC0gbG0ubG9nQWxrJG1vZGVsJHJlc2lkdWFscyAjIE1ha2UgYSBuZXcgY29sdW1uIGluIHRoZSBkYXRhIGZvciByYXcgcmVzaWR1YWwgdmFsdWVzDQpiYXNzLm91dCA8LSBiYXNzW2Jhc3MkSUQgJWluJSBjKDEsNDAsMiw1KSxdICAgICAgICAgICAjIE91dGxpZXJzIG9ubHkNCmJhc3Mub3V0DQpgYGANCg0KV2hlbiB3ZSBkaXJlY3RseSBleGFtaW5lIGluZmx1ZW5jZSB3aXRoIENvb2sncyBEaXN0YW5jZSB3ZSBzZWUgdGhhdCBsYWtlcyAxLDIsNSwgYW5kIDQwLCB0aGUgc2FtZSBsYWtlcyB3ZSBpZGVudGlmaWVkIGFzIG91dGxpZXJzIHBsdXMgQnJpY2sgbGFrZSwgaGF2ZSB0aGUgaGlnaGVzdCBDb29rJ3MgRGlzdGFuY2UuIExha2UgMSBhbmQgTGFrZSA0MCwgbmFtZWQgQWxsaWdhdG9yIExha2UgYW5kIFB1enpsZSBsYWtlIHJlc3BlY3RpdmVseSwgaGF2ZSBkcmFzdGljYWxseSBoaWdoZXIgQ29vaydzIGRpc3RhbmNlcyB0aGFuIG1vc3Qgb2YgdGhlIG90aGVyIGRhdGEuIFRoZXNlIHR3byBwb2ludHMgaGF2ZSB0aGUgaGlnaGVzdCBwb3RlbnRpYWwgdG8gYmUgaW5mbHVlbnRpYWwsIGJ1dCBuZWl0aGVyIHBvaW50IGhhcyBtdWNoIGxldmVyYWdlLiBCcmljayBMYWtlIGhhcyBhIG1vZGVyYXRlbHkgaGlnaCBsZXZlcmFnZSBhbmQgYSBtb2RlcmF0ZWx5IGhpZ2ggc3R1ZGVudGl6ZWQgcmVzaWR1YWwsIGFzIHdlbGwgYXMgYSByZWxhdGl2ZWx5IGhpZ2ggQ29vaydzIERpc3RhbmNlLiBCcmljayBsYWtlIHNlZW1zIHRvIGJlIGEgbW9kZXJhdGxleSBpbmZsdWVudGlhbCBwb2ludC4NCg0KYGBge3J9IA0Kb2xzX3Bsb3RfY29va3NkX2JhcihsbS5sb2dBbGskbW9kZWwpDQpgYGANCg0KSGVyZSBhcmUgdGhlIG91dGxpZXJzIChncmVlbikgYW5kIGhpZ2ggbGV2ZXJhZ2UgKHJlZCkgcG9pbnRzIG9uIG91ciBpbml0aWFsIHNjYXR0ZXJwbG90IChyZWd1bGFyIHBvaW50cyBpbiBibHVlKS4gQnJpY2sgbGFrZSBpcyBzaG93biBhcyBhIHllbGxvdyBkaWFtb25kLg0KDQpgYGB7cn0gDQpicmljayA8LSBiYXNzWzUsXQ0KY29sb3IuY29kZWQuMiA8LQ0KZ2dwbG90KGJhc3NbLTUsXSwgYWVzKHg9bG9nQWxrYWxpbml0eSx5PU1lcmN1cnkpKSArDQogIGdlb21fcG9pbnQoZGF0YT1iYXNzWy01LF0sIHNpemU9Myxjb2xvcj0iYmxhY2siLGZpbGw9ImJsdWUiLHNoYXBlPTIxKSsNCiAgZ2VvbV9wb2ludChkYXRhPWJhc3Mub3V0Wy0zLF0sc2l6ZT0zLGNvbG9yPSJibGFjayIsZmlsbD0iZ3JlZW4iLHNoYXBlPTIxKSsgIyBPdXRsaWVycw0KICBnZW9tX3BvaW50KGRhdGE9YmFzcy5sZXZbLTEsXSxzaXplPTMsY29sb3I9ImJsYWNrIixmaWxsPSJyZWQiLHNoYXBlPTIxKSsgICAjIEhpZ2ggbGV2ZXJhZ2UgcG9pbnRzDQogIGdlb21fcG9pbnQoZGF0YT1icmljayxzaXplPTMsY29sb3I9ImJsYWNrIixmaWxsPSJ5ZWxsb3ciLHNoYXBlPTIzKSsNCiAgeGxhYigibG4oTGFrZSBBbGthbGluaXR5KSAoIGluIG1nL0wpIikgKw0KICB5bGFiKCJUeXBpY2FsIEFtb3VudCBvZiBNZXJjdXJ5IGluIEJhc3MgKG1jZykiKSArDQogIGdndGl0bGUoIk1lcmN1cnkgQ29udGVudCB2ZXJzdXMgTGFrZSBBbGthbGluaXR5IikNCg0KZ2dwbG90bHkoY29sb3IuY29kZWQuMikNCmBgYA0KDQojIFExMSAtIEFkdmljZSBmb3IgdGhlIFB1YmxpYyAgDQogIA0KVG8gcHJlZGljdCB0aGUgcGxhdXNpYmxlIG1lcmN1cnkgdmFsdWVzIGZvciBsYXJnZW1vdXRoIGJhc3MgZnJvbSBhIG5ldyBsYWtlLCB3ZSB3aWxsIGNyZWF0ZSBhIHByZWRpY3Rpb24gaW50ZXJ2YWwgd2l0aCBhIDk5JSBjb25maWRlbmNlIGxldmVsLiBOb3RlIHRoYXQgYmVjYXVzZSBvdXIgcmVzaWR1YWxzIHdlcmUgcG9zaXRpdmVseSBza2V3ZWQsIHRoaXMgcHJlZGljdGlvbiBpbnRlcnZhbCBpcyBwcm9iYWJseSBnb2luZyB0byBjYXB0dXJlIGxvd2VyIG1lcmN1cnkgbGV2ZWxzIHRoYXQgaXMgc2hvdWxkIG5vdCBiZSBjYXB0dXJpbmcgYW5kIGl0IHdpbGwgbm90IGNhcHR1cmUgc29tZSBoaWdoZXIgbWVyY3VyeSBsZXZlbHMgdGhhdCBpcyBzaG91bGQgY2FwdHVyZS4gV2UgY29udGludWUgd2l0aCBjYXV0aW9uLg0KDQpgYGB7cn0NCm5ldy5sYWtlIDwtIGRhdGEuZnJhbWUobG9nQWxrYWxpbml0eT1jKGxvZyg0MCkpKQ0KcHJlZCA8LSBwcmVkaWN0KGxtLmxvZ0FsayRtb2RlbCxuZXdkYXRhPW5ldy5sYWtlLGludGVydmFsPSJwcmVkaWN0IixsZXZlbD0wLjk5KQ0KcHJlZA0KYGBgDQpJZiB0aGUgcmVzaWR1YWxzIHdlcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgd2Ugd291bGQgY29uY2x1ZGUgd2l0aCA5OSUgY29uZmlkZW5jZSB0aGF0IHRoZSByYW5nZSBvZiBwbGF1c2libGUgdmFsdWVzIGZvciBtZXJjdXJ5IGNvbnRlbnQgaW4gYSBsYXJnZW1vdXRoIGJhc3MgZnJvbSB0aGlzIG5ldyBsYWtlIGlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byAxLjAxMiBtY2cgb2YgbWVyY3VyeSAod2Ugd291bGQgaGF2ZSB0aGUgaW5jbHVzaXZlIGludGVydmFsIGZyb20gLTAuMjYgdG8gMS4wMSwgYnV0IGEgbmVnYXRpdmUgbWVyY3VyeSBsZXZlbCBpcyBub3QgaW50ZXJwcmV0YWJsZSkuIEhvd2V2ZXIgdGhlIHRydWUgZGlzdHJpYnV0aW9uIG9mIG91ciByZXNpZHVhbHMgc2VlbWVkIHBvc2l0aXZlbHkgc2tld2VkLCBub3QgTm9ybWFsIGFzIHdhcyBhc3N1bWVkIGluIGNhbGN1bGF0aW5nIHRoaXMgcHJlZGljdGlvbiBpbnRlcnZhbCwgc28gdGhpcyBpbnRlcnZhbCBpc24ndCBjYXB0dXJpbmcgc29tZSBoaWdoZXIgbWVyY3VyeSB2YWx1ZXMgdGhhdCBpdCBzaG91bGQuIEluIGFueSBjYXNlLCB0aGUgaW50ZXJ2YWwgYWxyZWFkeSBpbmNsdWRlZCAxIG1jZyBtZXJjdXJ5LCBzbyB3ZSB3b3VsZCBub3QgY29uY2x1ZGUgbGFyZ2Vtb3V0aCBiYXNzIGZyb20gdGhpcyBuZXcgbGFrZSB0byBiZSBzYWZlIHRvIGVhdCBhbnl3YXkuIFRoaXMgcGVyc29uIHNob3VsZCBub3QgZWF0IGxhcmdlbW91dGggYmFzcyBmcm9tIHRoaXMgbGFrZS4NCiAgDQpXZSBjYW4ndCBleHRlbmQgdGhpcyBjb25jbHVzaW9uIHRvIG90aGVyIGZpc2ggc3BlY2llcywgc28gdW5sZXNzIHRoZSBmaXNoIHRoYXQgdGhpcyBwZXJzb24gY2F1Z2h0IHdhcyBhbHNvIGEgbGFyZ2Vtb3V0aCBiYXNzIHdlIGRvbid0IGhhdmUgdGhlIHJpZ2h0IGluZm9ybWF0aW9uIHRvIGFjY3VyYXRlbHkgYW5zd2VyIHRoZWlyIHF1ZXN0aW9uLg0KICANCiMgUTEyIC0gU2FmZXR5IFRocmVzaG9sZCBmb3IgQWxrYWxpbml0eSAgIA0KICANCiAgDQpXZSdsbCBjb21wdXRlIHRoZSBwcmVkaWN0aW9uIHN0YW5kYXJkIGVycm9yLCAkc2UocHJlZGljdGlvbikkICwgYnkgdXNpbmcgdGhlIGludGVydmFsIGFib3ZlLiBBcyBhIHJlbWluZGVyLCB0aGlzIGlzIHRoZSBpbnRlcnZhbCB3ZSBodmU6DQoNCmBgYHtyfQ0KcHJlZA0KYGBgDQoNCg0KU28gd2UgaGF2ZSB0aGlzIGZvcm11bGE6IA0KICANClxbXHRleHR7UHJlZGljdGlvbiBJbnRlcnZhbH09IHByZWRpY3Rpb24gXHBtIHReKl97XGFscGhhLzIsbi0yfSBcdGltZXMgc2UocHJlZGljdGlvbilcXQ0KICANClxbXHRleHR7UEl9X3tcdGV4dHt1cHBlciBsaW1pdH19ID0gcHJlZGljdGlvbiArIHReKl97XGFscGhhLzIsbi0yfSBcdGltZXMgc2UocHJlZGljdGlvbilcXQ0KICANClxbMS4wMTI1NzEgXG11IGcgPSAwLjM3NDc1MDggKyB0Xipfe1xhbHBoYS8yLG4tMn0gXHRpbWVzIHNlKHByZWRpY3Rpb24pXF0NCiAgDQpXZSBoYXZlIGEgc2lnbmlmaWNhbmNlIGxldmVsIG9mICRcYWxwaGEkPTAuMDUgYW5kICRuLTIkPTUxIGRlZ3JlZXMgb2YgZnJlZWRvbSwgc28gd2UgY2FuIGZpbmQgb3VyIGNyaXRpY2FsIHZhbHVlICR0KiQgZWFzaWx5LiAgDQogIA0KYGBge3J9DQphbHBoYSA8LSAwLjA1DQpkZiA8LSBucm93KGJhc3MpLTINCnQuc3RhciA8LSBxdCgxLSAoYWxwaGEvMiksIGRmKQ0KcHJpbnQocGFzdGUoInQqOiIsIHQuc3RhcikpDQpgYGANCg0KICANClxbMS4wMTI1NzEgPSAwLjM3NDc1MDggKyAyLjAwNzU4NCBcdGltZXMgc2UocHJlZGljdGlvbilcXQ0KICANClxbMS4wMTI1NzEgPSAwLjM3NDc1MDggKyAyLjAwNzU4NCBcdGltZXMgc2UocHJlZGljdGlvbilcXQ0KICANClxbMC42Mzc4MjAyID0gIDIuMDA3NTg0IFx0aW1lcyBzZShwcmVkaWN0aW9uKVxdDQogICAgDQpcWzAuMzE3NzA1NCA9IHNlKHByZWRpY3Rpb24pXF0NCg0KTm93IHRoYXQgd2UgaGF2ZSBhIHNldCBjcml0aWNhbCB2YWx1ZSBhbmQgYSBzdGFuZGFyZCBlcnJvciBvZiBwcmVkaWN0aW9uLCB3ZSBjYW4gZmluZCB0aGUgcHJlZGljdGVkIG1lcmN1cnkgdmFsdWUgdGhhdCBoYXMgYSBwcmVkaWN0aW9uIGludGVydmFsIHdob3NlIHVwcGVyIGxpbWl0IGlzIDEgbWNnIG9mIG1lcmN1cnkuIFNvIHdlIGFyZSBzb2x2aW5nIGZvciAkcHJlZGljdGlvbiQgaW4gdGhlIGVxdWF0aW9uIGJlbG93LiANCiAgDQpcW1x0ZXh0e1BJfV97XHRleHR7dXBwZXIgbGltaXR9fSA9IHByZWRpY3Rpb24gKyB0Xipfe1xhbHBoYS8yLG4tMn0gXHRpbWVzIHNlKHByZWRpY3Rpb24pXF0NCiAgDQpcWzEgXG11IGcgPSBwcmVkaWN0aW9uICsgdF4qX3tcYWxwaGEvMixuLTJ9IFx0aW1lcyBzZShwcmVkaWN0aW9uKVxdDQogIA0KXFsxIFxtdSBnID0gcHJlZGljdGlvbiArIDIuMDA3NTg0IFx0aW1lcyAwLjMxNzcwNTRcXQ0KICANClxbMSBcbXUgZyA9IHByZWRpY3Rpb24gKyAwLjYzNzgyMDJcXQ0KDQpcWzAuMzYyMTc5OCBcbXUgZyA9IHByZWRpY3Rpb25cXQ0KICANCk5vdyB3ZSBzb2x2ZSBmb3IgdGhlIGFsa2FsaW5pdHkgb2YgYSBsYWtlIHdob3NlIGZpc2ggaGF2ZSBhIHByZWRpY3RlZCBtZXJjdXJ5IGNvbnRlbnQgb2YgfjAuMzYyIG1jZyBvZiBtZXJjdXJ5LiBUbyBzb2x2ZSBmb3IgdGhpcywgd2UgZ28gYmFjayB0byBvdXIgcmVncmVzc2lvbiBlcXVhdGlvbi4gDQogIA0KXFtcd2lkZWhhdHtNZXJjdXJ5IENvbnRlbnR9PTEuMTE3MysoLTAuMjAxMykgXHRpbWVzIFxsbihBbGthbGluaXR5KVxdDQogIA0KXFswLjM2MjE3OTggPSAxLjExNzMrKC0wLjIwMTMpIFx0aW1lcyBcbG4oQWxrYWxpbml0eSlcXQ0KICANClxbMC4zNjIxNzk4ID0gMS4xMTczKygtMC4yMDEzKSBcdGltZXMgXGxuKEFsa2FsaW5pdHkpXF0NCiAgDQpcWy0wLjc1NTEyMDIgPSAtMC4yMDEzIFx0aW1lcyBcbG4oQWxrYWxpbml0eSlcXQ0KICANClxbMy43NTEyMTggPSBcbG4oQWxrYWxpbml0eSlcXQ0KDQpcW2VeezMuNzUxMjE4fSA9IGVee1xsbihBbGthbGluaXR5KX1cXQ0KDQpcWzQyLjU3MjkgXHRleHR7bWcvTH0gPSBBbGthbGluaXR5XF0NCiAgDQpXZSB3b3VsZCBzZXQgdGhlIGN1dG9mZiBvZiB0aGUgbGFrZSBhbGthbGluaXR5IHRvIGJlIH4gNDIuNTcgbWcvTCAuDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=